Skip to content

Commit bc887b3

Browse files
Use similar types for self-referential generics instead of the exact canonical type (#83995)
- Take advantage of work done a few years ago to simplify the interaction with the interop subsystem - In the problematic case, simulate loads with two different types as instantiations which are unrelated in field layout, and see if they match up. Only enable this code in a few very small isolated parts of the runtime - Filter more of the type loader logic through the byvalue class cache which should improve performance a bit - Similarly when considering blittability, tweak the logic to use the special load path - Support for self-recursive generics is not enabled for static fields, as that requires a somewhat different tweak, and there is less apparent demand. (For that scenario self-referential generics really should support having fields of type T.) - Support for indirect self-recursive generics is also not enabled. The approach taken here is not practical for that, and there does not appear to be significant demand for that either. Fixes #6924
1 parent 735ddea commit bc887b3

17 files changed

+546
-179
lines changed

src/coreclr/debug/daccess/dacdbiimpl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4519,7 +4519,9 @@ void DacDbiInterfaceImpl::EnumerateModulesInAssembly(
45194519
// Debugger isn't notified of Resource / Inspection-only modules.
45204520
if (pDomainAssembly->GetModule()->IsVisibleToDebugger())
45214521
{
4522-
_ASSERTE(pDomainAssembly->IsLoaded());
4522+
// If domain assembly isn't yet loaded, just return
4523+
if (!pDomainAssembly->IsLoaded())
4524+
return;
45234525

45244526
VMPTR_DomainAssembly vmDomainAssembly = VMPTR_DomainAssembly::NullPtr();
45254527
vmDomainAssembly.SetHostPtr(pDomainAssembly);

src/coreclr/dlls/mscorrc/mscorrc.rc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ BEGIN
314314
IDS_CLASSLOAD_INLINE_ARRAY_LENGTH "InlineArrayAttribute requires that the length argument is greater than 0. Type: '%1'. Assembly: '%2'."
315315
IDS_CLASSLOAD_INLINE_ARRAY_EXPLICIT "InlineArrayAttribute cannot be applied to a type with explicit layout. Type: '%1'. Assembly: '%2'."
316316

317+
IDS_INVALID_RECURSIVE_GENERIC_FIELD_LOAD "Could not load type '%1' from assembly '%2' because of an invalid self-referential generic field."
318+
317319
#if FEATURE_COMINTEROP
318320
IDS_EE_CANNOTCAST_NOMARSHAL "The Windows Runtime Object can only be used in the threading context where it was created, because it implements INoMarshal or has MarshalingBehaviorAttribute(MarshalingType.None) set."
319321
#endif

src/coreclr/dlls/mscorrc/resource.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@
166166
#define IDS_CLASSLOAD_MI_BADRETURNTYPE 0x17a8
167167
#define IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL 0x17a9
168168

169+
#define IDS_INVALID_RECURSIVE_GENERIC_FIELD_LOAD 0x17aa
169170
#define IDS_CLASSLOAD_TOOMANYGENERICARGS 0x17ab
170171

171172
#define IDS_CLASSLOAD_INLINE_ARRAY_FIELD_COUNT 0x17ac

src/coreclr/vm/class.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,6 +2230,11 @@ class ApproxFieldDescIterator
22302230
SUPPORTS_DAC;
22312231
return m_totalFields - m_currField - 1;
22322232
}
2233+
int GetValueClassCacheIndex()
2234+
{
2235+
LIMITED_METHOD_CONTRACT;
2236+
return m_currField;
2237+
}
22332238
};
22342239

22352240
//

src/coreclr/vm/classlayoutinfo.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ namespace
461461
IMDInternalImport* pInternalImport,
462462
HENUMInternal* phEnumField,
463463
Module* pModule,
464+
mdTypeDef cl,
464465
ParseNativeTypeFlags nativeTypeFlags,
465466
const SigTypeContext* pTypeContext,
466467
BOOL* fDisqualifyFromManagedSequential,
@@ -477,6 +478,8 @@ namespace
477478
#endif
478479
)
479480
{
481+
STANDARD_VM_CONTRACT;
482+
480483
HRESULT hr;
481484
mdFieldDef fd;
482485
ULONG maxRid = pInternalImport->GetCountWithTokenKind(mdtFieldDef);
@@ -533,20 +536,45 @@ namespace
533536
}
534537
#endif
535538
MetaSig fsig(pCOMSignature, cbCOMSignature, pModule, pTypeContext, MetaSig::sigField);
536-
CorElementType corElemType = fsig.NextArgNormalized();
539+
CorElementType corElemType = fsig.NextArg();
540+
537541
TypeHandle typeHandleMaybe;
538542
if (corElemType == ELEMENT_TYPE_VALUETYPE) // Only look up the next element in the signature if it is a value type to avoid causing recursive type loads in valid scenarios.
539543
{
540-
typeHandleMaybe = fsig.GetLastTypeHandleThrowing(ClassLoader::LoadTypes,
541-
CLASS_LOAD_APPROXPARENTS,
542-
TRUE);
544+
SigPointer::HandleRecursiveGenericsForFieldLayoutLoad recursiveControl;
545+
recursiveControl.pModuleWithTokenToAvoidIfPossible = pModule;
546+
recursiveControl.tkTypeDefToAvoidIfPossible = cl;
547+
typeHandleMaybe = fsig.GetArgProps().GetTypeHandleThrowing(pModule,
548+
pTypeContext,
549+
ClassLoader::LoadTypes,
550+
CLASS_LOAD_APPROXPARENTS,
551+
TRUE, NULL, NULL, NULL,
552+
&recursiveControl);
553+
554+
if (typeHandleMaybe.IsNull())
555+
{
556+
// Everett C++ compiler can generate a TypeRef with RS=0
557+
// without respective TypeDef for unmanaged valuetypes,
558+
// referenced only by pointers to them.
559+
// In such case, GetTypeHandleThrowing returns null handle,
560+
// and we return E_T_VOID
561+
typeHandleMaybe = TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_VOID));
562+
}
563+
corElemType = typeHandleMaybe.AsMethodTable()->GetInternalCorElementType();
564+
if (corElemType != ELEMENT_TYPE_VALUETYPE)
565+
typeHandleMaybe = TypeHandle();
566+
}
567+
else if (corElemType == ELEMENT_TYPE_TYPEDBYREF)
568+
{
569+
typeHandleMaybe = TypeHandle(g_TypedReferenceMT);
543570
}
571+
544572
pFieldInfoArrayOut->m_placement = GetFieldPlacementInfo(corElemType, typeHandleMaybe);
545573
*fDisqualifyFromManagedSequential |= TypeHasGCPointers(corElemType, typeHandleMaybe);
546574
*fHasAutoLayoutField |= TypeHasAutoLayoutField(corElemType, typeHandleMaybe);
547575
*fHasInt128Field |= TypeHasInt128Field(corElemType, typeHandleMaybe);
548576

549-
if (!IsFieldBlittable(pModule, fd, fsig.GetArgProps(), pTypeContext, nativeTypeFlags))
577+
if (!IsFieldBlittable(pModule, fd, corElemType, typeHandleMaybe, nativeTypeFlags))
550578
*pIsBlittableOut = FALSE;
551579

552580
(*cInstanceFields)++;
@@ -705,6 +733,7 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing(
705733
pInternalImport,
706734
phEnumField,
707735
pModule,
736+
cl,
708737
nativeTypeFlags,
709738
pTypeContext,
710739
&fDisqualifyFromManagedSequential,

src/coreclr/vm/field.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,32 @@ UINT FieldDesc::LoadSize()
672672
return size;
673673
}
674674

675+
UINT FieldDesc::GetSize(MethodTable *pMTOfValueTypeField)
676+
{
677+
CONTRACTL
678+
{
679+
INSTANCE_CHECK;
680+
NOTHROW;
681+
GC_NOTRIGGER;
682+
MODE_ANY;
683+
FORBID_FAULT;
684+
}
685+
CONTRACTL_END
686+
687+
CorElementType type = GetFieldType();
688+
UINT size = GetSizeForCorElementType(type);
689+
if (size == (UINT) -1)
690+
{
691+
LOG((LF_CLASSLOADER, LL_INFO10000, "FieldDesc::GetSize %s::%s\n", GetApproxEnclosingMethodTable()->GetDebugClassName(), m_debugName));
692+
CONSISTENCY_CHECK(GetFieldType() == ELEMENT_TYPE_VALUETYPE);
693+
TypeHandle t = (pMTOfValueTypeField != NULL) ? TypeHandle(pMTOfValueTypeField) : LookupApproxFieldTypeHandle();
694+
_ASSERTE(!t.IsNull());
695+
size = t.GetMethodTable()->GetNumInstanceFieldBytes();
696+
}
697+
698+
return size;
699+
}
700+
675701
UINT FieldDesc::GetSize()
676702
{
677703
CONTRACTL

src/coreclr/vm/field.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ class FieldDesc
324324
// Return -1 if the type isn't loaded yet (i.e. if LookupFieldTypeHandle() would return null)
325325
UINT GetSize();
326326

327+
// If the field is a valuetype, then either pMTOfValueTypeField must not be NULL or LookupFieldTypeHandle() must not return null
328+
UINT GetSize(MethodTable *pMTOfValueTypeField);
329+
327330
// These routines encapsulate the operation of getting and setting
328331
// fields.
329332
void GetInstanceField(OBJECTREF o, VOID * pOutVal);

src/coreclr/vm/fieldmarshaler.cpp

Lines changed: 56 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,13 @@ VOID ParseNativeType(Module* pModule,
201201
bool IsFieldBlittable(
202202
Module* pModule,
203203
mdFieldDef fd,
204-
SigPointer fieldSig,
205-
const SigTypeContext* pTypeContext,
204+
CorElementType corElemType,
205+
TypeHandle valueTypeHandle,
206206
ParseNativeTypeFlags flags
207207
)
208208
{
209+
STANDARD_VM_CONTRACT;
210+
209211
PCCOR_SIGNATURE marshalInfoSig;
210212
ULONG marshalInfoSigLength;
211213

@@ -218,75 +220,63 @@ bool IsFieldBlittable(
218220

219221
bool isBlittable = false;
220222

221-
EX_TRY
223+
switch (corElemType)
222224
{
223-
TypeHandle valueTypeHandle;
224-
CorElementType corElemType = fieldSig.PeekElemTypeNormalized(pModule, pTypeContext, &valueTypeHandle);
225-
226-
switch (corElemType)
225+
case ELEMENT_TYPE_CHAR:
226+
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT && flags != ParseNativeTypeFlags::IsAnsi) || (nativeType == NATIVE_TYPE_I2) || (nativeType == NATIVE_TYPE_U2);
227+
break;
228+
case ELEMENT_TYPE_I1:
229+
case ELEMENT_TYPE_U1:
230+
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_I1) || (nativeType == NATIVE_TYPE_U1);
231+
break;
232+
case ELEMENT_TYPE_I2:
233+
case ELEMENT_TYPE_U2:
234+
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_I2) || (nativeType == NATIVE_TYPE_U2);
235+
break;
236+
case ELEMENT_TYPE_I4:
237+
case ELEMENT_TYPE_U4:
238+
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_I4) || (nativeType == NATIVE_TYPE_U4) || (nativeType == NATIVE_TYPE_ERROR);
239+
break;
240+
case ELEMENT_TYPE_I8:
241+
case ELEMENT_TYPE_U8:
242+
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_I8) || (nativeType == NATIVE_TYPE_U8);
243+
break;
244+
case ELEMENT_TYPE_R4:
245+
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_R4);
246+
break;
247+
case ELEMENT_TYPE_R8:
248+
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_R8);
249+
break;
250+
case ELEMENT_TYPE_I:
251+
case ELEMENT_TYPE_U:
252+
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_INT) || (nativeType == NATIVE_TYPE_UINT);
253+
break;
254+
case ELEMENT_TYPE_PTR:
255+
isBlittable = nativeType == NATIVE_TYPE_DEFAULT;
256+
break;
257+
case ELEMENT_TYPE_FNPTR:
258+
isBlittable = nativeType == NATIVE_TYPE_DEFAULT || nativeType == NATIVE_TYPE_FUNC;
259+
break;
260+
case ELEMENT_TYPE_VALUETYPE:
261+
if (nativeType != NATIVE_TYPE_DEFAULT && nativeType != NATIVE_TYPE_STRUCT)
227262
{
228-
case ELEMENT_TYPE_CHAR:
229-
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT && flags != ParseNativeTypeFlags::IsAnsi) || (nativeType == NATIVE_TYPE_I2) || (nativeType == NATIVE_TYPE_U2);
230-
break;
231-
case ELEMENT_TYPE_I1:
232-
case ELEMENT_TYPE_U1:
233-
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_I1) || (nativeType == NATIVE_TYPE_U1);
234-
break;
235-
case ELEMENT_TYPE_I2:
236-
case ELEMENT_TYPE_U2:
237-
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_I2) || (nativeType == NATIVE_TYPE_U2);
238-
break;
239-
case ELEMENT_TYPE_I4:
240-
case ELEMENT_TYPE_U4:
241-
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_I4) || (nativeType == NATIVE_TYPE_U4) || (nativeType == NATIVE_TYPE_ERROR);
242-
break;
243-
case ELEMENT_TYPE_I8:
244-
case ELEMENT_TYPE_U8:
245-
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_I8) || (nativeType == NATIVE_TYPE_U8);
246-
break;
247-
case ELEMENT_TYPE_R4:
248-
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_R4);
249-
break;
250-
case ELEMENT_TYPE_R8:
251-
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_R8);
252-
break;
253-
case ELEMENT_TYPE_I:
254-
case ELEMENT_TYPE_U:
255-
isBlittable = (nativeType == NATIVE_TYPE_DEFAULT) || (nativeType == NATIVE_TYPE_INT) || (nativeType == NATIVE_TYPE_UINT);
256-
break;
257-
case ELEMENT_TYPE_PTR:
258-
isBlittable = nativeType == NATIVE_TYPE_DEFAULT;
259-
break;
260-
case ELEMENT_TYPE_FNPTR:
261-
isBlittable = nativeType == NATIVE_TYPE_DEFAULT || nativeType == NATIVE_TYPE_FUNC;
262-
break;
263-
case ELEMENT_TYPE_VALUETYPE:
264-
if (nativeType != NATIVE_TYPE_DEFAULT && nativeType != NATIVE_TYPE_STRUCT)
265-
{
266-
isBlittable = false;
267-
}
268-
else if (valueTypeHandle.GetMethodTable() == CoreLibBinder::GetClass(CLASS__DECIMAL))
269-
{
270-
// The alignment requirements of the managed System.Decimal type do not match the native DECIMAL type.
271-
// As a result, a field of type System.Decimal can't be blittable.
272-
isBlittable = false;
273-
}
274-
else
275-
{
276-
isBlittable = valueTypeHandle.GetMethodTable()->IsBlittable();
277-
}
278-
break;
279-
default:
280263
isBlittable = false;
281-
break;
282264
}
265+
else if (valueTypeHandle.GetMethodTable() == CoreLibBinder::GetClass(CLASS__DECIMAL))
266+
{
267+
// The alignment requirements of the managed System.Decimal type do not match the native DECIMAL type.
268+
// As a result, a field of type System.Decimal can't be blittable.
269+
isBlittable = false;
270+
}
271+
else
272+
{
273+
isBlittable = valueTypeHandle.GetMethodTable()->IsBlittable();
274+
}
275+
break;
276+
default:
277+
isBlittable = false;
278+
break;
283279
}
284-
EX_CATCH
285-
{
286-
// We were unable to determine the native type, likely because there is a mutually recursive type reference
287-
// in this field's type. A mutually recursive object would never be blittable, so we don't need to do anything.
288-
}
289-
EX_END_CATCH(RethrowTerminalExceptions);
290280
return isBlittable;
291281
}
292282

src/coreclr/vm/fieldmarshaler.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ BOOL IsStructMarshalable(TypeHandle th);
5252
bool IsFieldBlittable(
5353
Module* pModule,
5454
mdFieldDef fd,
55-
SigPointer fieldSig,
56-
const SigTypeContext* pTypeContext,
55+
CorElementType corElemType,
56+
TypeHandle valueTypeHandle,
5757
ParseNativeTypeFlags flags
5858
);
5959

0 commit comments

Comments
 (0)