<!--
TODO: Remove pinned version once arcade supplies a compiler that enables the repo to compile.
-->
- <MicrosoftNetCompilersToolsetVersion>4.4.0-1.22315.13</MicrosoftNetCompilersToolsetVersion>
+ <MicrosoftNetCompilersToolsetVersion>4.4.0-1.22328.22</MicrosoftNetCompilersToolsetVersion>
<!-- SDK dependencies -->
<MicrosoftDotNetCompatibilityVersion>2.0.0-preview.4.22252.4</MicrosoftDotNetCompatibilityVersion>
<!-- Arcade dependencies -->
[System.Runtime.Versioning.NonVersionable] // This only applies to field layout
public ref partial struct TypedReference
{
- private readonly ByReference<byte> _value;
+ private readonly ref byte _value;
private readonly IntPtr _type;
public static unsafe object? ToObject(TypedReference value)
if (pMethodTable->IsValueType)
{
- result = RuntimeHelpers.Box(pMethodTable, ref value._value.Value);
+ result = RuntimeHelpers.Box(pMethodTable, ref value._value);
}
else
{
- result = Unsafe.As<byte, object>(ref value._value.Value);
+ result = Unsafe.As<byte, object>(ref value._value);
}
return result;
DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pArrayClass, ::g_pArrayClass)
DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pSZArrayHelperClass, ::g_pSZArrayHelperClass)
DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pNullableClass, ::g_pNullableClass)
-DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pByReferenceClass, ::g_pByReferenceClass)
DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pExceptionClass, ::g_pExceptionClass)
DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pThreadAbortExceptionClass, ::g_pThreadAbortExceptionClass)
DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pOutOfMemoryExceptionClass, ::g_pOutOfMemoryExceptionClass)
break;
case NI_Internal_Runtime_MethodTable_Of:
- case NI_System_ByReference_ctor:
- case NI_System_ByReference_get_Value:
case NI_System_Activator_AllocatorOf:
case NI_System_Activator_DefaultConstructorOf:
case NI_System_EETypePtr_EETypePtrOf:
break;
}
- // Implement ByReference Ctor. This wraps the assignment of the ref into a byref-like field
- // in a value type. The canonical example of this is Span<T>. In effect this is just a
- // substitution. The parameter byref will be assigned into the newly allocated object.
- case NI_System_ByReference_ctor:
- {
- // Remove call to constructor and directly assign the byref passed
- // to the call to the first slot of the ByReference struct.
- GenTree* op1 = impPopStack().val;
- GenTree* thisptr = newobjThis;
- CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(clsHnd, 0);
- GenTree* field = gtNewFieldRef(TYP_BYREF, fldHnd, thisptr, 0);
- GenTree* assign = gtNewAssignNode(field, op1);
- GenTree* byReferenceStruct = gtCloneExpr(thisptr->gtGetOp1());
- assert(byReferenceStruct != nullptr);
- impPushOnStack(byReferenceStruct, typeInfo(TI_STRUCT, clsHnd));
- retNode = assign;
- break;
- }
-
- // Implement ptr value getter for ByReference struct.
- case NI_System_ByReference_get_Value:
- {
- GenTree* op1 = impPopStack().val;
- CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(clsHnd, 0);
- GenTree* field = gtNewFieldRef(TYP_BYREF, fldHnd, op1, 0);
- retNode = field;
- break;
- }
-
case NI_System_Runtime_CompilerServices_RuntimeHelpers_CreateSpan:
{
retNode = impCreateSpanIntrinsic(sig);
result = NI_System_Activator_DefaultConstructorOf;
}
}
- else if (strcmp(className, "ByReference`1") == 0)
- {
- if (strcmp(methodName, ".ctor") == 0)
- {
- result = NI_System_ByReference_ctor;
- }
- else if (strcmp(methodName, "get_Value") == 0)
- {
- result = NI_System_ByReference_get_Value;
- }
- }
else if (strcmp(className, "Math") == 0 || strcmp(className, "MathF") == 0)
{
if (strcmp(methodName, "Abs") == 0)
NI_Array_Get,
NI_Array_Set,
- NI_System_ByReference_ctor,
- NI_System_ByReference_get_Value,
NI_System_Activator_AllocatorOf,
NI_System_Activator_DefaultConstructorOf,
NI_System_EETypePtr_EETypePtrOf,
public ref struct TypedReference
{
// Do not change the ordering of these fields. The JIT has a dependency on this layout.
- private readonly ByReference<byte> _value;
+ private readonly ref byte _value;
private readonly RuntimeTypeHandle _typeHandle;
private TypedReference(object target, int offset, RuntimeTypeHandle typeHandle)
{
- _value = new ByReference<byte>(ref Unsafe.Add<byte>(ref target.GetRawData(), offset));
+ _value = ref Unsafe.Add<byte>(ref target.GetRawData(), offset);
_typeHandle = typeHandle;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- return ref _value.Value;
+ return ref _value;
}
}
}
{
int result = 0;
- if (type.IsByReferenceOfT)
- {
- return MarkGcField(gcPtrs, CorInfoGCType.TYPE_GC_BYREF);
- }
-
foreach (var field in type.GetFields())
{
if (field.IsStatic)
type = asCorInfoType(fieldType);
}
- Debug.Assert(!fieldDesc.OwningType.IsByReferenceOfT ||
- fieldDesc.OwningType.GetKnownField("_value").FieldType.Category == TypeFlags.IntPtr);
- if (type == CorInfoType.CORINFO_TYPE_NATIVEINT && fieldDesc.OwningType.IsByReferenceOfT)
- {
- Debug.Assert(structType == null || *structType == null);
- Debug.Assert(fieldDesc.Offset.AsInt == 0);
- type = CorInfoType.CORINFO_TYPE_BYREF;
- }
-
return type;
}
return false;
}
- SystemVClassificationType fieldClassificationType;
- if (typeDesc.IsByReferenceOfT)
- {
- // ByReference<T> is a special type whose single IntPtr field holds a by-ref potentially interior pointer to GC
- // memory, so classify its field as such
- Debug.Assert(numIntroducedFields == 1);
- Debug.Assert(field.FieldType.IsWellKnownType(WellKnownType.IntPtr));
-
- fieldClassificationType = SystemVClassificationTypeIntegerByRef;
- }
- else
- {
- fieldClassificationType = TypeDef2SystemVClassification(field.FieldType);
- }
-
+ SystemVClassificationType fieldClassificationType = TypeDef2SystemVClassification(field.FieldType);
if (fieldClassificationType == SystemVClassificationTypeStruct)
{
bool inEmbeddedStructPrev = helper.InEmbeddedStruct;
{
SetFieldLayout(refMap, offset, _pointerSize, FieldLayoutTag.ORef);
}
- else if (field.FieldType.IsByRef || field.FieldType.IsByReferenceOfT)
+ else if (field.FieldType.IsByRef)
{
SetFieldLayout(refMap, offset, _pointerSize, FieldLayoutTag.ByRef);
}
"Exception",
"TypedReference",
- "ByReference`1",
};
public static IEnumerable<string> WellKnownTypeNames => s_wellKnownTypeNames;
// Initialize all well known types - it will save us from checking the name for each loaded type
for (int typeIndex = 0; typeIndex < _wellKnownTypes.Length; typeIndex++)
{
- // Require System.Object to be present as a minimal sanity check.
+ // Require System.Object to be present as a minimal sanity check.
// The set of required well-known types is not strictly defined since different .NET profiles implement different subsets.
MetadataType type = systemModule.GetType("System", s_wellKnownTypeNames[typeIndex], throwIfNotFound: typeIndex == (int)WellKnownType.Object);
if (type != null)
int typeIndex = (int)wellKnownType - 1;
DefType type = _wellKnownTypes[typeIndex];
- if (type == null && throwIfNotFound)
+ if (type == null && throwIfNotFound)
ThrowHelper.ThrowTypeLoadException("System", s_wellKnownTypeNames[typeIndex], SystemModule);
return type;
case WellKnownType.RuntimeMethodHandle:
case WellKnownType.RuntimeFieldHandle:
case WellKnownType.TypedReference:
- case WellKnownType.ByReferenceOfT:
flags = TypeFlags.ValueType;
break;
}
/// <summary>
- /// Gets a value indicating whether this is a generic definition, or
- /// an instance of System.ByReference`1.
- /// </summary>
- public bool IsByReferenceOfT
- {
- get
- {
- return this.GetTypeDefinition().IsWellKnownType(WellKnownType.ByReferenceOfT);
- }
- }
-
- /// <summary>
/// Gets a value indicating whether this is an array type (<see cref="ArrayType"/>).
/// Note this will return true for both multidimensional array types and vector types.
/// Use <see cref="IsSzArray"/> to check for vector types.
Exception,
TypedReference,
- ByReferenceOfT,
}
}
bool isBlittable = MarshalUtils.IsBlittableType(type);
// Blittable generics are allowed to be marshalled with the following exceptions:
- // * ByReference<T>: This represents an interior pointer and is not actually blittable
// * Nullable<T>: We don't want to be locked into the default behavior as we may want special handling later
// * Vector64<T>: Represents the __m64 ABI primitive which requires currently unimplemented handling
// * Vector128<T>: Represents the __m128 ABI primitive which requires currently unimplemented handling
// We can't block these types for field scenarios for back-compat reasons.
if (type.HasInstantiation && !isField && (!isBlittable
- || InteropTypes.IsSystemByReference(context, type)
|| InteropTypes.IsSystemSpan(context, type)
|| InteropTypes.IsSystemReadOnlySpan(context, type)
|| InteropTypes.IsSystemNullable(context, type)
return IsCoreNamedType(context, type, "System", "ArgIterator");
}
- public static bool IsSystemByReference(TypeSystemContext context, TypeDesc type)
- {
- return IsCoreNamedType(context, type, "System", "ByReference`1");
- }
-
public static bool IsSystemSpan(TypeSystemContext context, TypeDesc type)
{
return IsCoreNamedType(context, type, "System", "Span`1");
return;
}
- if (method.OwningType.IsByReferenceOfT && (method.IsConstructor || method.Name == "get_Value"))
- {
- return;
- }
-
if (IsEETypePtrOf(method))
{
if (runtimeDeterminedMethod.IsRuntimeDeterminedExactMethod)
using Internal.TypeSystem;
-// The GCRef map is used to encode GC type of arguments for callsites. Logically, it is sequence <pos, token> where pos is
+// The GCRef map is used to encode GC type of arguments for callsites. Logically, it is sequence <pos, token> where pos is
// position of the reference in the stack frame and token is type of GC reference (one of GCREFMAP_XXX values).
//
-// - The encoding always starts at the byte boundary. The high order bit of each byte is used to signal end of the encoding
+// - The encoding always starts at the byte boundary. The high order bit of each byte is used to signal end of the encoding
// stream. The last byte has the high order bit zero. It means that there are 7 useful bits in each byte.
// - "pos" is always encoded as delta from previous pos.
-// - The basic encoding unit is two bits. Values 0, 1 and 2 are the common constructs (skip single slot, GC reference, interior
-// pointer). Value 3 means that extended encoding follows.
-// - The extended information is integer encoded in one or more four bit blocks. The high order bit of the four bit block is
+// - The basic encoding unit is two bits. Values 0, 1 and 2 are the common constructs (skip single slot, GC reference, interior
+// pointer). Value 3 means that extended encoding follows.
+// - The extended information is integer encoded in one or more four bit blocks. The high order bit of the four bit block is
// used to signal the end.
// - For x86, the encoding starts by size of the callee poped stack. The size is encoded using the same mechanism as above (two bit
// basic encoding, with extended encoding for large values).
private int _pendingByte;
/// <summary>
- /// Number of bits in pending byte. Note that the trailing zero bits are not written out,
+ /// Number of bits in pending byte. Note that the trailing zero bits are not written out,
/// so this can be more than 7.
/// </summary>
private int _bits;
private void FindByRefPointerOffsetsInByRefLikeObject(TypeDesc type, ArgDestination argDest, int delta, CORCOMPILE_GCREFMAP_TOKENS[] frame)
{
- if (type.IsByReferenceOfT || type.IsByRef)
+ if (type.IsByRef)
{
argDest.GcMark(frame, delta, interior: true);
return;
size = fieldType.GetElementSize().AsInt;
alignment = size;
}
- else if (fieldType.IsByRef || fieldType.IsByRefLike || fieldType.IsByReferenceOfT)
+ else if (fieldType.IsByRef || fieldType.IsByRefLike)
{
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, fieldDesc.OwningType);
}
public override string ToString()
{
return
- $"""
- FormatVersion: {FormatVersion}
- Runtime: {Runtime}
- Os: {Os}
- Arch: {Arch}
+$@"
+FormatVersion: {FormatVersion}
+Runtime: {Runtime}
+Os: {Os}
+Arch: {Arch}
- """;
+";
}
public static MibcConfig FromKeyValueMap(Dictionary<string, string> kvMap)
{
public ref struct ByRefLikeStruct
{
- ByReference<object> ByRef;
+ ref object ByRef;
}
public struct NotByRefLike
public abstract class ValueType { }
public abstract class Enum : ValueType { }
public struct Nullable<T> where T : struct { }
-
+
public sealed class String { }
public abstract class Array : System.Collections.IList { }
public abstract class Delegate { }
public ref struct TypedReference
{
- private readonly ByReference<byte> _value;
+ private readonly ref byte _value;
private readonly RuntimeTypeHandle _typeHandle;
}
-
- public ref struct ByReference<T> { }
}
namespace System.Collections
.class public sequential ansi sealed beforefieldinit IsByRefLike.InvalidStruct
extends [CoreTestAssembly]System.ValueType
{
- .field private valuetype [CoreTestAssembly]System.ByReference`1<object> ByRef
+ .field private object& ByRef
}
.class public auto ansi beforefieldinit IsByRefLike.InvalidClass1
extends [CoreTestAssembly]System.Object
{
- .field private valuetype [CoreTestAssembly]System.ByReference`1<object> ByRef
+ .field private object& ByRef
}
.class public auto ansi beforefieldinit IsByRefLike.InvalidClass2
extends [CoreTestAssembly]System.Object
{
- .custom instance void [CoreTestAssembly]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( 01 00 00 00 )
- .field private valuetype [CoreTestAssembly]System.ByReference`1<object> ByRef
+ .custom instance void [CoreTestAssembly]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( 01 00 00 00 )
+ .field private object& ByRef
}
MetadataType t = _testModule.GetType("Explicit", "Class1");
// With 64bit, there should be 8 bytes for the System.Object EE data pointer +
- // 10 bytes up until the offset of the char field + the char size of 2 + we
+ // 10 bytes up until the offset of the char field + the char size of 2 + we
// round up the whole instance size to the next pointer size (+4) = 24
Assert.Equal(24, t.InstanceByteCount.AsInt);
if (f.Name == "Lol")
{
- // First field after base class, with offset 0 so it should lie on the byte count of
+ // First field after base class, with offset 0 so it should lie on the byte count of
// the base class = 20
Assert.Equal(20, f.Offset.AsInt);
}
}
{
- DefType type = _context.GetWellKnownType(WellKnownType.ByReferenceOfT);
- Assert.True(type.IsByRefLike);
- }
-
- {
DefType type = _testModule.GetType("IsByRefLike", "ByRefLikeStruct");
Assert.True(type.IsByRefLike);
}
<PackageReleaseNotes>$(Description)</PackageReleaseNotes>
<RootNamespace>Microsoft.Diagnostics.Tools.Pgo</RootNamespace>
<RollForward>Major</RollForward>
+ <!-- TODO Remove the language version limitation when the reference assemblies
+ are updated with the new scoped keyword. -->
+ <LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
// the SZArrayHelper class here.
g_pSZArrayHelperClass = CoreLibBinder::GetClass(CLASS__SZARRAYHELPER);
- // Load ByReference class
- //
- // NOTE: ByReference<T> must be the first by-ref-like system type to be loaded,
- // because MethodTable::ClassifyEightBytesWithManagedLayout depends on it.
- g_pByReferenceClass = CoreLibBinder::GetClass(CLASS__BYREFERENCE);
-
// Load Nullable class
g_pNullableClass = CoreLibBinder::GetClass(CLASS__NULLABLE);
#define g_ArrayClassName "System.Array"
#define g_NullableName "Nullable`1"
-#define g_ByReferenceName "ByReference`1"
#define g_CollectionsEnumerableItfName "System.Collections.IEnumerable"
#define g_CollectionsEnumeratorClassName "System.Collections.IEnumerator"
DEFINE_CLASS(NULLABLE, System, Nullable`1)
-DEFINE_CLASS(BYREFERENCE, System, ByReference`1)
+DEFINE_CLASS(BYREFERENCE, System, ByReference)
DEFINE_METHOD(BYREFERENCE, CTOR, .ctor, NoSig)
-DEFINE_METHOD(BYREFERENCE, GET_VALUE, get_Value, NoSig)
+DEFINE_FIELD(BYREFERENCE, VALUE, Value)
DEFINE_CLASS(SPAN, System, Span`1)
DEFINE_METHOD(SPAN, CTOR_PTR_INT, .ctor, IM_VoidPtr_Int_RetVoid)
DEFINE_METHOD(SPAN, GET_ITEM, get_Item, IM_Int_RetRefT)
{
switch (intrinsicId)
{
- case NI_System_ByReference_ctor:
- DoByReferenceCtor();
- didIntrinsic = true;
- break;
- case NI_System_ByReference_get_Value:
- DoByReferenceValue();
- didIntrinsic = true;
- break;
#if INTERP_ILSTUBS
case NI_System_StubHelpers_GetStubContext:
OpStackSet<void*>(m_curStackHt, GetStubContext());
OpStackTypeSet(ind, InterpreterType(CORINFO_TYPE_CLASS));
}
-void Interpreter::DoByReferenceCtor()
-{
- CONTRACTL {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- } CONTRACTL_END;
-
- // Note 'this' is not passed on the operand stack...
- _ASSERTE(m_curStackHt > 0);
- _ASSERTE(m_callThisArg != NULL);
- unsigned valInd = m_curStackHt - 1;
- CorInfoType valCit = OpStackTypeGet(valInd).ToCorInfoType();
-
-#ifdef _DEBUG
- if (valCit != CORINFO_TYPE_BYREF)
- {
- VerificationError("ByReference<T>.ctor called with non-byref value.");
- }
-#endif // _DEBUG
-
-#if INTERP_TRACING
- if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
- {
- fprintf(GetLogFile(), " ByReference<T>.ctor -- intrinsic\n");
- }
-#endif // INTERP_TRACING
-
- GCX_FORBID();
- void** thisPtr = reinterpret_cast<void**>(m_callThisArg);
- void* val = OpStackGet<void*>(valInd);
- *thisPtr = val;
- m_curStackHt--;
-}
-
-void Interpreter::DoByReferenceValue()
-{
- CONTRACTL {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- } CONTRACTL_END;
-
- _ASSERTE(m_curStackHt > 0);
- unsigned slot = m_curStackHt - 1;
- CorInfoType thisCit = OpStackTypeGet(slot).ToCorInfoType();
-
-#ifdef _DEBUG
- if (thisCit != CORINFO_TYPE_BYREF)
- {
- VerificationError("ByReference<T>.get_Value called with non-byref this");
- }
-#endif // _DEBUG
-
-#if INTERP_TRACING
- if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
- {
- fprintf(GetLogFile(), " ByReference<T>.getValue -- intrinsic\n");
- }
-#endif // INTERP_TRACING
-
- GCX_FORBID();
- void** thisPtr = OpStackGet<void**>(slot);
- void* value = *thisPtr;
- OpStackSet<void*>(slot, value);
- OpStackTypeSet(slot, InterpreterType(CORINFO_TYPE_BYREF));
-}
-
void Interpreter::DoSIMDHwAccelerated()
{
CONTRACTL {
void DoStringLength();
void DoStringGetChar();
void DoGetTypeFromHandle();
- void DoByReferenceCtor();
- void DoByReferenceValue();
void DoSIMDHwAccelerated();
void DoGetIsSupported();
_ASSERTE(pMT->IsValueType());
- if (pMT->HasSameTypeDefAs(g_pByReferenceClass))
- return MarkGCField(gcPtrs, TYPE_GC_BYREF);
-
unsigned result = 0;
ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS);
for (FieldDesc *pFD = fieldIterator.Next(); pFD != NULL; pFD = fieldIterator.Next())
TypeHandle clsHnd = TypeHandle();
FieldDesc* field = (FieldDesc*) fieldHnd;
CorElementType type = field->GetFieldType();
-
- if (type == ELEMENT_TYPE_I)
- {
- PTR_MethodTable enclosingMethodTable = field->GetApproxEnclosingMethodTable();
- if (enclosingMethodTable->IsByRefLike() && enclosingMethodTable->HasSameTypeDefAs(g_pByReferenceClass))
- {
- _ASSERTE(field->GetOffset() == 0);
- return CORINFO_TYPE_BYREF;
- }
- }
-
if (!CorTypeInfo::IsPrimitiveType(type))
{
PCCOR_SIGNATURE sig;
numIntroducedFields = GetNumInstanceFieldBytes() / pFieldStart->GetSize();
}
- // System types are loaded before others, so ByReference<T> would be loaded before Span<T> or any other type that has a
- // ByReference<T> field. ByReference<T> is the first by-ref-like system type to be loaded (see
- // SystemDomain::LoadBaseSystemClasses), so if the current method table is marked as by-ref-like and g_pByReferenceClass is
- // null, it must be the initial load of ByReference<T>.
- bool isThisByReferenceOfT = IsByRefLike() && (g_pByReferenceClass == nullptr || HasSameTypeDefAs(g_pByReferenceClass));
-
for (unsigned int fieldIndex = 0; fieldIndex < numIntroducedFields; fieldIndex++)
{
FieldDesc* pField;
}
CorElementType fieldType = pField->GetFieldType();
-
- SystemVClassificationType fieldClassificationType;
- if (isThisByReferenceOfT)
- {
- // ByReference<T> is a special type whose single IntPtr field holds a by-ref potentially interior pointer to GC
- // memory, so classify its field as such
- _ASSERTE(numIntroducedFields == 1);
- _ASSERTE(fieldType == CorElementType::ELEMENT_TYPE_I);
- fieldClassificationType = SystemVClassificationTypeIntegerByRef;
- }
- else
- {
- fieldClassificationType = CorInfoType2UnixAmd64Classification(fieldType);
- }
+ SystemVClassificationType fieldClassificationType = CorInfoType2UnixAmd64Classification(fieldType);
#ifdef _DEBUG
LPCUTF8 fieldName;
STANDARD_VM_CONTRACT;
_ASSERTE(pMT->IsByRefLike());
- // ByReference<T> is an indication for a byref field, treat it as such.
- if (pMT->HasSameTypeDefAs(g_pByReferenceClass))
- return MarkTagType(pFieldLayout, TARGET_POINTER_SIZE, byref);
-
ExplicitClassTrust explicitClassTrust;
ExplicitFieldTrust::TrustLevel trust;
if (g_pNullableClass != NULL)
{
- _ASSERTE(g_pByReferenceClass != NULL);
- _ASSERTE(g_pByReferenceClass->IsByRefLike());
-
_ASSERTE(g_pNullableClass->IsNullable());
// Pre-compute whether the class is a Nullable<T> so that code:Nullable::IsNullableType is efficient
break;
// Blittable generics are allowed to be marshalled with the following exceptions:
- // * ByReference<T>: This represents an interior pointer and is not actually blittable
// * Nullable<T>: We don't want to be locked into the default behavior as we may want special handling later
// * Vector64<T>: Represents the __m64 ABI primitive which requires currently unimplemented handling
// * Vector128<T>: Represents the __m128 ABI primitive which requires currently unimplemented handling
if (m_pMT->HasInstantiation() && !IsFieldScenario()
&& (!m_pMT->IsBlittable()
|| (m_pMT->HasSameTypeDefAs(g_pNullableClass)
- || m_pMT->HasSameTypeDefAs(g_pByReferenceClass)
|| m_pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__SPAN))
|| m_pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__READONLY_SPAN))
|| m_pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR64T))
_ASSERTE(pMT != nullptr);
_ASSERTE(pMT->IsByRefLike());
- if (pMT->HasSameTypeDefAs(g_pByReferenceClass))
- {
- Report(baseOffset);
- return;
- }
-
ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS);
for (FieldDesc* pFD = fieldIterator.Next(); pFD != NULL; pFD = fieldIterator.Next())
{
#endif // _DEBUG
}
-// Get TypeHandle for ByReference<System.Byte>
-static TypeHandle GetByReferenceOfByteType()
+static TypeHandle GetByReferenceType()
{
- TypeHandle byteTH(CoreLibBinder::GetElementType(ELEMENT_TYPE_U1));
- Instantiation byteInst(&byteTH, 1);
- TypeHandle th = TypeHandle(CoreLibBinder::GetClass(CLASS__BYREFERENCE)).Instantiate(byteInst);
+ TypeHandle th = TypeHandle(CoreLibBinder::GetClass(CLASS__BYREFERENCE));
return th;
}
-// Get MethodDesc* for ByReference<System.Byte>::get_Value
-static MethodDesc* GetByReferenceOfByteValueGetter()
+static FieldDesc* GetByReferenceValueField()
{
- MethodDesc* getter = CoreLibBinder::GetMethod(METHOD__BYREFERENCE__GET_VALUE);
- getter =
- MethodDesc::FindOrCreateAssociatedMethodDesc(
- getter,
- GetByReferenceOfByteType().GetMethodTable(),
- false,
- Instantiation(),
- TRUE);
-
- return getter;
+ FieldDesc* pFD = CoreLibBinder::GetField(FIELD__BYREFERENCE__VALUE);
+ return pFD;
}
-// Get MethodDesc* for ByReference<System.Byte>::.ctor
-static MethodDesc* GetByReferenceOfByteCtor()
+static MethodDesc* GetByReferenceOfCtor()
{
- MethodDesc* ctor = CoreLibBinder::GetMethod(METHOD__BYREFERENCE__CTOR);
- ctor =
- MethodDesc::FindOrCreateAssociatedMethodDesc(
- ctor,
- GetByReferenceOfByteType().GetMethodTable(),
- false,
- Instantiation(),
- TRUE);
-
- return ctor;
+ MethodDesc* pMD = CoreLibBinder::GetMethod(METHOD__BYREFERENCE__CTOR);
+ return pMD;
}
void TailCallHelp::EmitLoadTyHnd(ILCodeStream* stream, TypeHandle tyHnd)
{
if (tyHnd.IsByRef())
- stream->EmitCALL(stream->GetToken(GetByReferenceOfByteValueGetter()), 1, 1);
+ stream->EmitLDFLD(stream->GetToken(GetByReferenceValueField()));
else
stream->EmitLDOBJ(stream->GetToken(tyHnd));
}
{
if (tyHnd.IsByRef())
{
- stream->EmitNEWOBJ(stream->GetToken(GetByReferenceOfByteCtor()), 1);
- stream->EmitSTOBJ(stream->GetToken(GetByReferenceOfByteType()));
+ stream->EmitNEWOBJ(stream->GetToken(GetByReferenceOfCtor()), 1);
+ stream->EmitSTOBJ(stream->GetToken(GetByReferenceType()));
}
else
{
GPTR_IMPL(MethodTable, g_pArrayClass);
GPTR_IMPL(MethodTable, g_pSZArrayHelperClass);
GPTR_IMPL(MethodTable, g_pNullableClass);
-GPTR_IMPL(MethodTable, g_pByReferenceClass);
GPTR_IMPL(MethodTable, g_pExceptionClass);
GPTR_IMPL(MethodTable, g_pThreadAbortExceptionClass);
GPTR_IMPL(MethodTable, g_pOutOfMemoryExceptionClass);
GPTR_DECL(MethodTable, g_pArrayClass);
GPTR_DECL(MethodTable, g_pSZArrayHelperClass);
GPTR_DECL(MethodTable, g_pNullableClass);
-GPTR_DECL(MethodTable, g_pByReferenceClass);
GPTR_DECL(MethodTable, g_pExceptionClass);
GPTR_DECL(MethodTable, g_pThreadAbortExceptionClass);
GPTR_DECL(MethodTable, g_pOutOfMemoryExceptionClass);
<NoWarn>$(NoWarn);SA1205;CA1845</NoWarn>
<EnableTrimAnalyzer Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETFramework'">false</EnableTrimAnalyzer>
<IsPackable>true</IsPackable>
+ <!-- Lifetime rules introduced in C# 11 impact scenarios in net6 framework -->
+ <LangVersion Condition="'$(TargetFramework)' == 'net6.0'">10</LangVersion>
<PackageDescription>Provides Classes that allow you to decouple code logging rich (unserializable) diagnostics/telemetry (e.g. framework) from code that consumes it (e.g. tools)
Commonly Used Types:
public static bool TryParse(System.ReadOnlySpan<byte> source, out ulong value, out int bytesConsumed, char standardFormat = '\0') { throw null; }
}
}
-namespace System.Runtime.CompilerServices
-{
- // See src\libraries\System.Private.CoreLib\src\System\Runtime\CompilerServices\LifetimeAnnotationAttribute.cs
- [System.AttributeUsageAttribute(System.AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
- internal sealed class LifetimeAnnotationAttribute : System.Attribute
- {
- public LifetimeAnnotationAttribute(bool isRefScoped, bool isValueScoped) { throw null; }
- public bool IsRefScoped { get { throw null; } }
- public bool IsValueScoped { get { throw null; } }
- }
-}
+
namespace System.Runtime.InteropServices
{
public static partial class MemoryMarshal
public static System.ReadOnlySpan<TTo> Cast<TFrom, TTo>(System.ReadOnlySpan<TFrom> span) where TFrom : struct where TTo : struct { throw null; }
public static System.Span<TTo> Cast<TFrom, TTo>(System.Span<TFrom> span) where TFrom : struct where TTo : struct { throw null; }
public static System.Memory<T> CreateFromPinnedArray<T>(T[]? array, int start, int length) { throw null; }
- public static System.ReadOnlySpan<T> CreateReadOnlySpan<T>([System.Runtime.CompilerServices.LifetimeAnnotation(true, false)] ref T reference, int length) { throw null; }
+ public static System.ReadOnlySpan<T> CreateReadOnlySpan<T>(scoped ref T reference, int length) { throw null; }
[System.CLSCompliant(false)]
public static unsafe ReadOnlySpan<byte> CreateReadOnlySpanFromNullTerminated(byte* value) { throw null; }
[System.CLSCompliant(false)]
public static unsafe ReadOnlySpan<char> CreateReadOnlySpanFromNullTerminated(char* value) { throw null; }
- public static System.Span<T> CreateSpan<T>([System.Runtime.CompilerServices.LifetimeAnnotation(true, false)] ref T reference, int length) { throw null; }
+ public static System.Span<T> CreateSpan<T>(scoped ref T reference, int length) { throw null; }
public static ref T GetArrayDataReference<T>(T[] array) { throw null; }
public static ref byte GetArrayDataReference(System.Array array) { throw null; }
- public static ref T GetReference<T>([System.Runtime.CompilerServices.LifetimeAnnotation(false, true)] System.ReadOnlySpan<T> span) { throw null; }
- public static ref T GetReference<T>([System.Runtime.CompilerServices.LifetimeAnnotation(false, true)] System.Span<T> span) { throw null; }
+ public static ref T GetReference<T>(System.ReadOnlySpan<T> span) { throw null; }
+ public static ref T GetReference<T>(System.Span<T> span) { throw null; }
public static T Read<T>(System.ReadOnlySpan<byte> source) where T : struct { throw null; }
public static System.Collections.Generic.IEnumerable<T> ToEnumerable<T>(System.ReadOnlyMemory<T> memory) { throw null; }
public static bool TryGetArray<T>(System.ReadOnlyMemory<T> memory, out System.ArraySegment<T> segment) { throw null; }
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DefaultInterpolatedStringHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IteratorStateMachineAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ITuple.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\LifetimeAnnotationAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\LoadHint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodCodeType.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodImplAttribute.cs" />
namespace System
{
- // ByReference<T> is meant to be used to represent "ref T" fields. It is working
- // around lack of first class support for byref fields in C# and IL. The JIT and
- // type loader has special handling for it that turns it into a thin wrapper around ref T.
+ // ByReference is meant to be used to represent a tracked reference in cases where C#
+ // proves difficult. See use in Reflection.
[NonVersionable]
- internal readonly ref struct ByReference<T>
+ internal readonly ref struct ByReference
{
-#pragma warning disable CA1823, 169 // private field '{blah}' is never used
- private readonly IntPtr _value;
-#pragma warning restore CA1823, 169
+ public readonly ref byte Value;
+ public ByReference(ref byte value) => Value = ref value;
-#pragma warning disable IDE0060
- [Intrinsic]
- public ByReference(ref T value)
- {
- // Implemented as a JIT intrinsic - This default implementation is for
- // completeness and to provide a concrete error if called via reflection
- // or if intrinsic is missed.
- throw new PlatformNotSupportedException();
- }
-#pragma warning restore IDE0060
-
-#pragma warning disable CA1822 // Mark members as static
- public ref T Value
- {
- // Implemented as a JIT intrinsic - This default implementation is for
- // completeness and to provide a concrete error if called via reflection
- // or if the intrinsic is missed.
- [Intrinsic]
- get => throw new PlatformNotSupportedException();
- }
-#pragma warning restore CA1822
+ public static ByReference Create<T>(ref T p) => new ByReference(ref Unsafe.As<T, byte>(ref p));
}
}
// The pos should point to a quote character. This method will
// append to the result StringBuilder the string enclosed by the quote character.
//
- internal static int ParseQuoteString(ReadOnlySpan<char> format, int pos, ref ValueStringBuilder result)
+ internal static int ParseQuoteString(scoped ReadOnlySpan<char> format, int pos, ref ValueStringBuilder result)
{
//
// NOTE : pos will be the index of the quote character in the 'format' string.
// Actions: Format the DateTime instance using the specified format.
//
private static void FormatCustomized(
- DateTime dateTime, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, TimeSpan offset, ref ValueStringBuilder result)
+ DateTime dateTime, scoped ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, TimeSpan offset, ref ValueStringBuilder result)
{
Calendar cal = dtfi.Calendar;
}
/// <summary>Format the TimeSpan instance using the specified format.</summary>
- private static void FormatCustomized(TimeSpan value, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, ref ValueStringBuilder result)
+ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, ref ValueStringBuilder result)
{
Debug.Assert(dtfi != null);
public readonly ref struct ReadOnlySpan<T>
{
/// <summary>A byref or a native ptr.</summary>
- internal readonly ByReference<T> _reference;
+ internal readonly ref T _reference;
/// <summary>The number of elements this ReadOnlySpan contains.</summary>
private readonly int _length;
return; // returns default
}
- _reference = new ByReference<T>(ref MemoryMarshal.GetArrayDataReference(array));
+ _reference = ref MemoryMarshal.GetArrayDataReference(array);
_length = array.Length;
}
ThrowHelper.ThrowArgumentOutOfRangeException();
#endif
- _reference = new ByReference<T>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */));
+ _reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */);
_length = length;
}
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
- _reference = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
+ _reference = ref Unsafe.As<byte, T>(ref *(byte*)pointer);
_length = length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ReadOnlySpan(in T reference)
{
- _reference = new ByReference<T>(ref Unsafe.AsRef(in reference));
+ _reference = ref Unsafe.AsRef(in reference);
_length = 1;
}
{
Debug.Assert(length >= 0);
- _reference = new ByReference<T>(ref reference);
+ _reference = ref reference;
_length = length;
}
{
if ((uint)index >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();
- return ref Unsafe.Add(ref _reference.Value, (nint)(uint)index /* force zero-extension */);
+ return ref Unsafe.Add(ref _reference, (nint)(uint)index /* force zero-extension */);
}
}
{
// Ensure that the native code has just one forward branch that is predicted-not-taken.
ref T ret = ref Unsafe.NullRef<T>();
- if (_length != 0) ret = ref _reference.Value;
+ if (_length != 0) ret = ref _reference;
return ref ret;
}
if ((uint)_length <= (uint)destination.Length)
{
- Buffer.Memmove(ref destination._reference.Value, ref _reference.Value, (uint)_length);
+ Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
}
else
{
bool retVal = false;
if ((uint)_length <= (uint)destination.Length)
{
- Buffer.Memmove(ref destination._reference.Value, ref _reference.Value, (uint)_length);
+ Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
retVal = true;
}
return retVal;
/// </summary>
public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right) =>
left._length == right._length &&
- Unsafe.AreSame<T>(ref left._reference.Value, ref right._reference.Value);
+ Unsafe.AreSame<T>(ref left._reference, ref right._reference);
/// <summary>
/// For <see cref="ReadOnlySpan{Char}"/>, returns a new instance of string that represents the characters pointed to by the span.
{
if (typeof(T) == typeof(char))
{
- return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _reference.Value), _length));
+ return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _reference), _length));
}
return $"System.ReadOnlySpan<{typeof(T).Name}>[{_length}]";
}
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
- return new ReadOnlySpan<T>(ref Unsafe.Add(ref _reference.Value, (nint)(uint)start /* force zero-extension */), _length - start);
+ return new ReadOnlySpan<T>(ref Unsafe.Add(ref _reference, (nint)(uint)start /* force zero-extension */), _length - start);
}
/// <summary>
ThrowHelper.ThrowArgumentOutOfRangeException();
#endif
- return new ReadOnlySpan<T>(ref Unsafe.Add(ref _reference.Value, (nint)(uint)start /* force zero-extension */), length);
+ return new ReadOnlySpan<T>(ref Unsafe.Add(ref _reference, (nint)(uint)start /* force zero-extension */), length);
}
/// <summary>
return Array.Empty<T>();
var destination = new T[_length];
- Buffer.Memmove(ref MemoryMarshal.GetArrayDataReference(destination), ref _reference.Value, (uint)_length);
+ Buffer.Memmove(ref MemoryMarshal.GetArrayDataReference(destination), ref _reference, (uint)_length);
return destination;
}
}
il.Emit(OpCodes.Add);
}
- il.Emit(OpCodes.Call, Methods.ByReferenceOfByte_Value()); // This can be replaced by ldfld once byref fields are available in C#
+ il.Emit(OpCodes.Ldfld, Methods.ByReferenceOfByte_Value());
RuntimeType parameterType = (RuntimeType)parameters[i].ParameterType;
if (!parameterType.IsByRef)
private static class Methods
{
- private static MethodInfo? s_ByReferenceOfByte_Value;
- public static MethodInfo ByReferenceOfByte_Value() =>
- s_ByReferenceOfByte_Value ??= typeof(ByReference<byte>).GetMethod("get_Value")!;
+ private static FieldInfo? s_ByReferenceOfByte_Value;
+ public static FieldInfo ByReferenceOfByte_Value() =>
+ s_ByReferenceOfByte_Value ??= typeof(ByReference).GetField("Value")!;
private static MethodInfo? s_ThrowHelper_Throw_NullReference_InvokeNullRefReturned;
public static MethodInfo ThrowHelper_Throw_NullReference_InvokeNullRefReturned() =>
(sigType.IsByRef && arg.GetType() == RuntimeTypeHandle.GetElementType(sigType)) ||
((sigType.IsEnum || arg.GetType().IsEnum) && RuntimeType.GetUnderlyingType((RuntimeType)arg.GetType()) == RuntimeType.GetUnderlyingType(sigType)));
#endif
- ByReference<byte> valueTypeRef = new(ref copyOfParameters[i]!.GetRawData());
- *(ByReference<byte>*)(byrefParameters + i) = valueTypeRef;
+ ByReference valueTypeRef = ByReference.Create(ref copyOfParameters[i]!.GetRawData());
+ *(ByReference*)(byrefParameters + i) = valueTypeRef;
}
else
{
- ByReference<object?> objRef = new(ref copyOfParameters[i]);
- *(ByReference<object?>*)(byrefParameters + i) = objRef;
+ ByReference objRef = ByReference.Create(ref copyOfParameters[i]);
+ *(ByReference*)(byrefParameters + i) = objRef;
}
}
}
[StructLayout(LayoutKind.Sequential)]
private protected ref struct StackAllocatedByRefs
{
- internal ByReference<byte> _arg0;
+ internal ref byte _arg0;
#pragma warning disable CA1823, CS0169, IDE0051 // accessed via 'CheckArguments' ref arithmetic
- private ByReference<byte> _arg1;
- private ByReference<byte> _arg2;
- private ByReference<byte> _arg3;
+ private ref byte _arg1;
+ private ref byte _arg2;
+ private ref byte _arg3;
#pragma warning restore CA1823, CS0169, IDE0051
}
#endif
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-
-namespace System.Runtime.CompilerServices
-{
- /// <summary>
- /// This type is defined until we consume the C# 11 compiler.
- /// </summary>
- /// <remarks>
- /// Also remove in the reference assemblies.
- /// </remarks>
- [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
- internal sealed class LifetimeAnnotationAttribute : Attribute
- {
- public LifetimeAnnotationAttribute(bool isRefScoped, bool isValueScoped)
- {
- IsRefScoped = isRefScoped;
- IsValueScoped = isValueScoped;
- }
- public bool IsRefScoped { get; }
- public bool IsValueScoped { get; }
- }
-}
/// <summary>
/// Reinterprets the given location as a reference to a value of type <typeparamref name="T"/>.
/// </summary>
+ /// <remarks>The lifetime of the reference will not be validated when using this API.</remarks>
[Intrinsic]
// CoreCLR:METHOD__UNSAFE__AS_REF_IN
// AOT:AsRef
// Mono:AsRef
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ref T AsRef<T>([LifetimeAnnotation(true, false)] in T source)
+ public static ref T AsRef<T>(scoped in T source)
{
throw new PlatformNotSupportedException();
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
/// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced.
/// </summary>
- public static ref T GetReference<T>([LifetimeAnnotation(false, true)] Span<T> span) => ref span._reference.Value;
+ public static ref T GetReference<T>(Span<T> span) => ref span._reference;
/// <summary>
/// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to the location where the 0th element
/// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced.
/// </summary>
- public static ref T GetReference<T>([LifetimeAnnotation(false, true)] ReadOnlySpan<T> span) => ref span._reference.Value;
+ public static ref T GetReference<T>(ReadOnlySpan<T> span) => ref span._reference;
/// <summary>
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to fake non-null pointer. Such a reference can be used
/// for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static unsafe ref T GetNonNullPinnableReference<T>(Span<T> span) => ref (span.Length != 0) ? ref span._reference.Value : ref Unsafe.AsRef<T>((void*)1);
+ internal static unsafe ref T GetNonNullPinnableReference<T>(Span<T> span) => ref (span.Length != 0) ? ref Unsafe.AsRef<T>(in span._reference) : ref Unsafe.AsRef<T>((void*)1);
/// <summary>
/// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to fake non-null pointer. Such a reference
/// can be used for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static unsafe ref T GetNonNullPinnableReference<T>(ReadOnlySpan<T> span) => ref (span.Length != 0) ? ref span._reference.Value : ref Unsafe.AsRef<T>((void*)1);
+ internal static unsafe ref T GetNonNullPinnableReference<T>(ReadOnlySpan<T> span) => ref (span.Length != 0) ? ref Unsafe.AsRef<T>(in span._reference) : ref Unsafe.AsRef<T>((void*)1);
/// <summary>
/// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
}
return new Span<TTo>(
- ref Unsafe.As<TFrom, TTo>(ref span._reference.Value),
+ ref Unsafe.As<TFrom, TTo>(ref span._reference),
toLength);
}
/// <param name="reference">A reference to data.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
/// <returns>A span representing the specified reference and length.</returns>
- /// <remarks>The lifetime of the returned span will not be validated for safety by span-aware languages.</remarks>
+ /// <remarks>
+ /// This method should be used with caution. It is dangerous because the length argument is not checked.
+ /// Even though the ref is annotated as scoped, it will be stored into the returned span, and the lifetime
+ /// of the returned span will not be validated for safety, even by span-aware languages.
+ /// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Span<T> CreateSpan<T>([LifetimeAnnotation(true, false)] ref T reference, int length) => new Span<T>(ref reference, length);
+ public static Span<T> CreateSpan<T>(scoped ref T reference, int length) =>
+ new Span<T>(ref Unsafe.AsRef(in reference), length);
/// <summary>
/// Creates a new read-only span over a portion of a regular managed object. This can be useful
/// <param name="reference">A reference to data.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
/// <returns>A read-only span representing the specified reference and length.</returns>
- /// <remarks>The lifetime of the returned span will not be validated for safety by span-aware languages.</remarks>
+ /// <remarks>
+ /// This method should be used with caution. It is dangerous because the length argument is not checked.
+ /// Even though the ref is annotated as scoped, it will be stored into the returned span, and the lifetime
+ /// of the returned span will not be validated for safety, even by span-aware languages.
+ /// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<T> CreateReadOnlySpan<T>([LifetimeAnnotation(true, false)] ref T reference, int length) => new ReadOnlySpan<T>(ref reference, length);
+ public static ReadOnlySpan<T> CreateReadOnlySpan<T>(scoped ref T reference, int length) =>
+ new ReadOnlySpan<T>(ref Unsafe.AsRef(in reference), length);
/// <summary>Creates a new read-only span for a null-terminated string.</summary>
/// <param name="value">The pointer to the null-terminated string of characters.</param>
public readonly ref struct Span<T>
{
/// <summary>A byref or a native ptr.</summary>
- internal readonly ByReference<T> _reference;
+ internal readonly ref T _reference;
/// <summary>The number of elements this Span contains.</summary>
private readonly int _length;
if (!typeof(T).IsValueType && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
- _reference = new ByReference<T>(ref MemoryMarshal.GetArrayDataReference(array));
+ _reference = ref MemoryMarshal.GetArrayDataReference(array);
_length = array.Length;
}
ThrowHelper.ThrowArgumentOutOfRangeException();
#endif
- _reference = new ByReference<T>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */));
+ _reference = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)start /* force zero-extension */);
_length = length;
}
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
- _reference = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
+ _reference = ref Unsafe.As<byte, T>(ref *(byte*)pointer);
_length = length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Span(ref T reference)
{
- _reference = new ByReference<T>(ref reference);
+ _reference = ref reference;
_length = 1;
}
{
Debug.Assert(length >= 0);
- _reference = new ByReference<T>(ref reference);
+ _reference = ref reference;
_length = length;
}
{
if ((uint)index >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();
- return ref Unsafe.Add(ref _reference.Value, (nint)(uint)index /* force zero-extension */);
+ return ref Unsafe.Add(ref _reference, (nint)(uint)index /* force zero-extension */);
}
}
{
// Ensure that the native code has just one forward branch that is predicted-not-taken.
ref T ret = ref Unsafe.NullRef<T>();
- if (_length != 0) ret = ref _reference.Value;
+ if (_length != 0) ret = ref _reference;
return ref ret;
}
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
- SpanHelpers.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _reference.Value), (uint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint)));
+ SpanHelpers.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _reference), (uint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint)));
}
else
{
- SpanHelpers.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _reference.Value), (uint)_length * (nuint)Unsafe.SizeOf<T>());
+ SpanHelpers.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _reference), (uint)_length * (nuint)Unsafe.SizeOf<T>());
}
}
// The runtime eventually calls memset, which can efficiently support large buffers.
// We don't need to check IsReferenceOrContainsReferences because no references
// can ever be stored in types this small.
- Unsafe.InitBlockUnaligned(ref Unsafe.As<T, byte>(ref _reference.Value), Unsafe.As<T, byte>(ref value), (uint)_length);
+ Unsafe.InitBlockUnaligned(ref Unsafe.As<T, byte>(ref _reference), Unsafe.As<T, byte>(ref value), (uint)_length);
}
else
{
// Call our optimized workhorse method for all other types.
- SpanHelpers.Fill(ref _reference.Value, (uint)_length, value);
+ SpanHelpers.Fill(ref _reference, (uint)_length, value);
}
}
if ((uint)_length <= (uint)destination.Length)
{
- Buffer.Memmove(ref destination._reference.Value, ref _reference.Value, (uint)_length);
+ Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
}
else
{
bool retVal = false;
if ((uint)_length <= (uint)destination.Length)
{
- Buffer.Memmove(ref destination._reference.Value, ref _reference.Value, (uint)_length);
+ Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
retVal = true;
}
return retVal;
/// </summary>
public static bool operator ==(Span<T> left, Span<T> right) =>
left._length == right._length &&
- Unsafe.AreSame<T>(ref left._reference.Value, ref right._reference.Value);
+ Unsafe.AreSame<T>(ref left._reference, ref right._reference);
/// <summary>
/// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/>
/// </summary>
public static implicit operator ReadOnlySpan<T>(Span<T> span) =>
- new ReadOnlySpan<T>(ref span._reference.Value, span._length);
+ new ReadOnlySpan<T>(ref span._reference, span._length);
/// <summary>
/// For <see cref="Span{Char}"/>, returns a new instance of string that represents the characters pointed to by the span.
{
if (typeof(T) == typeof(char))
{
- return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _reference.Value), _length));
+ return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _reference), _length));
}
return $"System.Span<{typeof(T).Name}>[{_length}]";
}
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
- return new Span<T>(ref Unsafe.Add(ref _reference.Value, (nint)(uint)start /* force zero-extension */), _length - start);
+ return new Span<T>(ref Unsafe.Add(ref _reference, (nint)(uint)start /* force zero-extension */), _length - start);
}
/// <summary>
ThrowHelper.ThrowArgumentOutOfRangeException();
#endif
- return new Span<T>(ref Unsafe.Add(ref _reference.Value, (nint)(uint)start /* force zero-extension */), length);
+ return new Span<T>(ref Unsafe.Add(ref _reference, (nint)(uint)start /* force zero-extension */), length);
}
/// <summary>
return Array.Empty<T>();
var destination = new T[_length];
- Buffer.Memmove(ref MemoryMarshal.GetArrayDataReference(destination), ref _reference.Value, (uint)_length);
+ Buffer.Memmove(ref MemoryMarshal.GetArrayDataReference(destination), ref _reference, (uint)_length);
return destination;
}
}
{
if ((uint)Length <= (uint)destination.Length)
{
- Buffer.Memmove(ref destination._reference.Value, ref _firstChar, (uint)Length);
+ Buffer.Memmove(ref destination._reference, ref _firstChar, (uint)Length);
}
else
{
bool retVal = false;
if ((uint)Length <= (uint)destination.Length)
{
- Buffer.Memmove(ref destination._reference.Value, ref _firstChar, (uint)Length);
+ Buffer.Memmove(ref destination._reference, ref _firstChar, (uint)Length);
retVal = true;
}
return retVal;
return !standardName.IsEmpty && !standardOffset.IsEmpty;
}
- private static ReadOnlySpan<char> TZif_ParsePosixName(ReadOnlySpan<char> posixFormat, ref int index)
+ private static ReadOnlySpan<char> TZif_ParsePosixName(ReadOnlySpan<char> posixFormat, scoped ref int index)
{
bool isBracketEnclosed = index < posixFormat.Length && posixFormat[index] == '<';
if (isBracketEnclosed)
}
}
- private static ReadOnlySpan<char> TZif_ParsePosixOffset(ReadOnlySpan<char> posixFormat, ref int index) =>
+ private static ReadOnlySpan<char> TZif_ParsePosixOffset(ReadOnlySpan<char> posixFormat, scoped ref int index) =>
TZif_ParsePosixString(posixFormat, ref index, c => !char.IsDigit(c) && c != '+' && c != '-' && c != ':');
- private static void TZif_ParsePosixDateTime(ReadOnlySpan<char> posixFormat, ref int index, out ReadOnlySpan<char> date, out ReadOnlySpan<char> time)
+ private static void TZif_ParsePosixDateTime(ReadOnlySpan<char> posixFormat, scoped ref int index, out ReadOnlySpan<char> date, out ReadOnlySpan<char> time)
{
time = null;
}
}
- private static ReadOnlySpan<char> TZif_ParsePosixDate(ReadOnlySpan<char> posixFormat, ref int index) =>
+ private static ReadOnlySpan<char> TZif_ParsePosixDate(ReadOnlySpan<char> posixFormat, scoped ref int index) =>
TZif_ParsePosixString(posixFormat, ref index, c => c == '/' || c == ',');
- private static ReadOnlySpan<char> TZif_ParsePosixTime(ReadOnlySpan<char> posixFormat, ref int index) =>
+ private static ReadOnlySpan<char> TZif_ParsePosixTime(ReadOnlySpan<char> posixFormat, scoped ref int index) =>
TZif_ParsePosixString(posixFormat, ref index, c => c == ',');
- private static ReadOnlySpan<char> TZif_ParsePosixString(ReadOnlySpan<char> posixFormat, ref int index, Func<char, bool> breakCondition)
+ private static ReadOnlySpan<char> TZif_ParsePosixString(ReadOnlySpan<char> posixFormat, scoped ref int index, Func<char, bool> breakCondition)
{
int startIndex = index;
for (; index < posixFormat.Length; index++)
throw new NotSupportedException(SR.NotSupported_NYI);
}
- internal bool IsNull => Unsafe.IsNullRef(ref _value.Value) && _type == IntPtr.Zero;
+ internal bool IsNull => Unsafe.IsNullRef(ref _value) && _type == IntPtr.Zero;
public static Type GetTargetType(TypedReference value)
{
[Theory]
[MemberData(nameof(GetUninitializedObject_ByRefLikeType_NetCore_TestData))]
- [SkipOnTargetFramework(~TargetFrameworkMonikers.Netcoreapp, "Some runtimes don't support or recognise Span<T>, ReadOnlySpan<T> or ByReference<T> as ref types.")]
+ [SkipOnTargetFramework(~TargetFrameworkMonikers.Netcoreapp, "Some runtimes don't support or recognise Span<T> or ReadOnlySpan<T> as ref types.")]
public void GetUninitializedObject_ByRefLikeType_NetCore_ThrowsNotSupportedException(Type type)
{
Assert.Throws<NotSupportedException>(() => FormatterServices.GetUninitializedObject(type));
object? this[int index] { get; }
int Length { get; }
}
- // See src\libraries\System.Private.CoreLib\src\System\Runtime\CompilerServices\LifetimeAnnotationAttribute.cs
- [System.AttributeUsageAttribute(System.AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
- internal sealed class LifetimeAnnotationAttribute : System.Attribute
- {
- public LifetimeAnnotationAttribute(bool isRefScoped, bool isValueScoped) { throw null; }
- public bool IsRefScoped { get { throw null; } }
- public bool IsValueScoped { get { throw null; } }
- }
public enum LoadHint
{
Default = 0,
public unsafe static void* AsPointer<T>(ref T value) { throw null; }
[System.CLSCompliantAttribute(false)]
public unsafe static ref T AsRef<T>(void* source) { throw null; }
- public static ref T AsRef<T>([System.Runtime.CompilerServices.LifetimeAnnotation(true, false)] in T source) { throw null; }
+ public static ref T AsRef<T>(scoped in T source) { throw null; }
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("o")]
public static T? As<T>(object? o) where T : class? { throw null; }
public static ref TTo As<TFrom, TTo>(ref TFrom source) { throw null; }
#region sync with object-internals.h
#pragma warning disable CA1823 // used by runtime
private readonly RuntimeTypeHandle type;
- private readonly ByReference<byte> _value;
+ private readonly ref byte _value;
private readonly IntPtr _type;
#pragma warning restore CA1823
#endregion
}
static gboolean
-class_is_byreference (MonoClass* klass)
-{
- const char* klass_name_space = m_class_get_name_space (klass);
- const char* klass_name = m_class_get_name (klass);
- MonoImage* klass_image = m_class_get_image (klass);
- gboolean in_corlib = klass_image == mono_defaults.corlib;
-
- if (in_corlib &&
- !strcmp (klass_name_space, "System") &&
- !strcmp (klass_name, "ByReference`1")) {
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
type_has_ref_fields (MonoType *ftype)
{
if (m_type_is_byref (ftype) || (MONO_TYPE_ISSTRUCT (ftype) && class_has_ref_fields (mono_class_from_mono_type_internal (ftype))))
return TRUE;
- /* Check for the ByReference`1 type */
- if (MONO_TYPE_ISSTRUCT (ftype)) {
- MonoClass* klass = mono_class_from_mono_type_internal (ftype);
- return class_is_byreference (klass);
- }
-
return FALSE;
}
} else {
int align = 0;
int size = mono_type_size (field->type, &align);
- guint8 type = type_has_references (klass, ftype) ? 1 : (m_type_is_byref (ftype) || class_is_byreference (klass)) ? 2 : 3;
+ guint8 type = type_has_references (klass, ftype) ? 1 : m_type_is_byref (ftype) ? 2 : 3;
// Mark the bytes used by this fields type based on if it contains references or not.
// Make sure there are no overlaps between object and non-object fields.
case MONO_MARSHAL_CONV_NONE: {
int t;
- //XXX a byref field!?!? that's not allowed! and worse, it might miss a WB
- g_assert (!m_type_is_byref (ftype));
- if (ftype->type == MONO_TYPE_I || ftype->type == MONO_TYPE_U) {
+ if (m_type_is_byref (ftype) || ftype->type == MONO_TYPE_I || ftype->type == MONO_TYPE_U) {
mono_mb_emit_ldloc (mb, 1);
mono_mb_emit_ldloc (mb, 0);
mono_mb_emit_byte (mb, CEE_LDIND_I);
if (tracing > 1) { \
output_indent (); \
char *mn = mono_method_full_name (frame->imethod->method, FALSE); \
- char *disasm = mono_interp_dis_mintop ((gint32)(ip - frame->imethod->code), TRUE, ip + 1, *ip); \
- g_print ("(%p) %s -> %s\n", mono_thread_internal_current (), mn, disasm); \
+ g_print ("(%p) %s -> IL_%04x: %-10s\n", mono_thread_internal_current (), mn, (gint32)(ip - frame->imethod->code), mono_interp_opname (*ip)); \
g_free (mn); \
- g_free (disasm); \
}
#else
#define DUMP_INSTR()
!strcmp (klass_name, "SpanHelpers") &&
!strcmp (tm, "ClearWithReferences")) {
*op = MINT_INTRINS_CLEAR_WITH_REFERENCES;
- } else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "ByReference`1")) {
- g_assert (!strcmp (tm, "get_Value"));
- *op = MINT_LDIND_I;
} else if (in_corlib && !strcmp (klass_name_space, "System") && !strcmp (klass_name, "Marvin")) {
if (!strcmp (tm, "Block")) {
InterpInst *ldloca2 = td->last_ins;
td->last_ins->flags |= INTERP_INST_FLAG_CALL;
td->last_ins->info.call_args = call_args;
} else if (m_class_get_image (klass) == mono_defaults.corlib &&
- !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
- !strcmp (m->name, ".ctor")) {
- /* public ByReference(ref T value) */
- MONO_PROFILER_RAISE (inline_method, (td->rtm->method, m));
- g_assert (csignature->hasthis && csignature->param_count == 1);
- td->sp--;
- /* We already have the vt on top of the stack. Just do a dummy mov that should be optimized out */
- interp_add_ins (td, MINT_MOV_P);
- interp_ins_set_sreg (td->last_ins, td->sp [0].local);
- push_type_vt (td, klass, mono_class_value_size (klass, NULL));
- interp_ins_set_dreg (td->last_ins, td->sp [-1].local);
- } else if (m_class_get_image (klass) == mono_defaults.corlib &&
(!strcmp (m_class_get_name (m->klass), "Span`1") ||
!strcmp (m_class_get_name (m->klass), "ReadOnlySpan`1")) &&
csignature->param_count == 2 &&
mono_class_init_internal (klass);
MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
- mt = mint_type (m_class_get_byval_arg (field_klass));
+ mt = mint_type (ftype);
int field_size = mono_class_value_size (field_klass, NULL);
int obj_size = mono_class_value_size (klass, NULL);
obj_size = ALIGN_TO (obj_size, MINT_VT_ALIGNMENT);
MonoInst*
mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
{
- const char* cmethod_klass_name_space = m_class_get_name_space (cmethod->klass);
- const char* cmethod_klass_name = m_class_get_name (cmethod->klass);
- MonoImage *cmethod_klass_image = m_class_get_image (cmethod->klass);
- gboolean in_corlib = cmethod_klass_image == mono_defaults.corlib;
MonoInst *ins = NULL;
/* Required intrinsics are always used even with -O=-intrins */
- if (in_corlib &&
- !strcmp (cmethod_klass_name_space, "System") &&
- !strcmp (cmethod_klass_name, "ByReference`1")) {
- /* public ByReference(ref T value) */
- g_assert (fsig->hasthis && fsig->param_count == 1);
- EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [0]->dreg, 0, args [1]->dreg);
- return ins;
- }
if (!(cfg->opt & MONO_OPT_INTRINS))
- return NULL;
+ return ins;
#ifdef MONO_ARCH_SIMD_INTRINSICS
if (cfg->opt & MONO_OPT_SIMD) {
}
#endif
- return NULL;
+ return ins;
}
static MonoInst*
gboolean in_corlib = cmethod_klass_image == mono_defaults.corlib;
/* Required intrinsics are always used even with -O=-intrins */
- if (in_corlib &&
- !strcmp (cmethod_klass_name_space, "System") &&
- !strcmp (cmethod_klass_name, "ByReference`1") &&
- !strcmp (cmethod->name, "get_Value")) {
- g_assert (fsig->hasthis && fsig->param_count == 0);
- int dreg = alloc_preg (cfg);
- EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, args [0]->dreg, 0);
- return ins;
- }
if (!(cfg->opt & MONO_OPT_INTRINS))
return NULL;