Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,18 @@ public static void StructWithNestedOverriddenNotBitwiseComparableTest()
Assert.Equal(obj1.GetHashCode(), obj2.GetHashCode());
}

[Fact]
public static void StructWithSelfReferencingGenericFieldPassedToGenericMethodTest()
{
var self = new StructWithSelfReferencingGenericField { value = new GenericFieldBox<StructWithSelfReferencingGenericField> { value = "self" } };
Assert.Equal("self", Identity(self).value.value);

var mutual = new StructWithMutualGenericFieldA { value = new GenericFieldBox<StructWithMutualGenericFieldB> { value = "mutual" } };
Assert.Equal("mutual", Identity(mutual).value.value);
}

private static T Identity<T>(T value) => value;

public struct S
{
public int x;
Expand Down Expand Up @@ -443,5 +455,25 @@ public struct StructWithNestedOverriddenNotBitwiseComparable
public StructOverriddenNotBitwiseComparable value1;
public StructOverriddenNotBitwiseComparable value2;
}

public struct GenericFieldBox<T>
{
public object value;
}

public struct StructWithSelfReferencingGenericField
{
public GenericFieldBox<StructWithSelfReferencingGenericField> value;
}

public struct StructWithMutualGenericFieldA
{
public GenericFieldBox<StructWithMutualGenericFieldB> value;
}

public struct StructWithMutualGenericFieldB
{
public GenericFieldBox<StructWithMutualGenericFieldA> value;
}
}
}
49 changes: 39 additions & 10 deletions src/mono/mono/mini/mini-generic-sharing.c
Original file line number Diff line number Diff line change
Expand Up @@ -1157,19 +1157,35 @@ static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_5, "Mono", "ValueTuple`5");
static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_6, "Mono", "ValueTuple`6");
static GENERATE_GET_CLASS_WITH_CACHE (valuetuple_7, "Mono", "ValueTuple`7");

/*
* Tracks the valuetypes whose layout is currently being normalized by get_wrapper_shared_vtype ().
* A struct can reference itself through a type argument of a field type, e.g.
* struct S { Box<S> f; }, which would otherwise recurse without bound.
*/
typedef struct _WrapperSharedVtypeFrame WrapperSharedVtypeFrame;
struct _WrapperSharedVtypeFrame {
MonoClass *klass;
WrapperSharedVtypeFrame *parent;
int depth;
};

/* Bounds expansion for types which grow on every level, e.g. struct S<T> { Box<S<S<T>>> f; } */
#define WRAPPER_SHARED_VTYPE_MAX_DEPTH 16

static MonoType*
get_wrapper_shared_type (MonoType *t);
static MonoType*
get_wrapper_shared_type_full (MonoType *t, gboolean field);
get_wrapper_shared_type_full (MonoType *t, gboolean field, WrapperSharedVtypeFrame *frame);

/*
* get_wrapper_shared_vtype:
*
* Return an instantiation of one of the Mono.ValueTuple types with the same
* layout as the valuetype KLASS.
* layout as the valuetype KLASS. Return NULL if the type should not be shared,
* including when its layout is already being normalized further up the stack.
*/
static MonoType*
get_wrapper_shared_vtype (MonoType *t)
get_wrapper_shared_vtype (MonoType *t, WrapperSharedVtypeFrame *parent)
{
ERROR_DECL (error);
MonoGenericContext ctx;
Expand All @@ -1184,6 +1200,19 @@ get_wrapper_shared_vtype (MonoType *t)
// FIXME: Map 1 member structs to primitive types on platforms where its supported

klass = mono_class_from_mono_type_internal (t);

if (parent && parent->depth >= WRAPPER_SHARED_VTYPE_MAX_DEPTH)
return NULL;
for (WrapperSharedVtypeFrame *f = parent; f; f = f->parent) {
if (f->klass == klass)
return NULL;
}

WrapperSharedVtypeFrame frame;
frame.klass = klass;
frame.parent = parent;
frame.depth = parent ? parent->depth + 1 : 1;

/* Under mono, auto and sequential layout are the same for valuetypes, see mono_class_layout_fields () */
if (((mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK) != TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT) &&
((mono_class_get_flags (klass) & TYPE_ATTRIBUTE_LAYOUT_MASK) != TYPE_ATTRIBUTE_AUTO_LAYOUT))
Expand Down Expand Up @@ -1233,7 +1262,7 @@ get_wrapper_shared_vtype (MonoType *t)
while ((field = mono_class_get_fields_internal (klass, &iter))) {
if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
continue;
MonoType *ftype = get_wrapper_shared_type_full (field->type, TRUE);
MonoType *ftype = get_wrapper_shared_type_full (field->type, TRUE, &frame);
if (m_class_is_byreflike (mono_class_from_mono_type_internal (ftype)))
/* Cannot inflate generic params with byreflike types */
return NULL;
Expand Down Expand Up @@ -1326,7 +1355,7 @@ get_wrapper_shared_vtype (MonoType *t)
* Return a type which is handled identically wrt to calling conventions as T.
*/
static MonoType*
get_wrapper_shared_type_full (MonoType *t, gboolean is_field)
get_wrapper_shared_type_full (MonoType *t, gboolean is_field, WrapperSharedVtypeFrame *frame)
{
if (m_type_is_byref (t))
return mono_class_get_byref_type (mono_defaults.int_class);
Expand Down Expand Up @@ -1401,14 +1430,14 @@ get_wrapper_shared_type_full (MonoType *t, gboolean is_field)
if (inst) {
g_assert (inst->type_argc < 16);
for (guint i = 0; i < inst->type_argc; ++i)
args [i] = get_wrapper_shared_type_full (inst->type_argv [i], TRUE);
args [i] = get_wrapper_shared_type_full (inst->type_argv [i], TRUE, frame);
ctx.class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
}
inst = orig_ctx->method_inst;
if (inst) {
g_assert (inst->type_argc < 16);
for (guint i = 0; i < inst->type_argc; ++i)
args [i] = get_wrapper_shared_type_full (inst->type_argv [i], TRUE);
args [i] = get_wrapper_shared_type_full (inst->type_argv [i], TRUE, frame);
ctx.method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
}
klass = mono_class_inflate_generic_class_checked (mono_class_get_generic_class (klass)->container_class, &ctx, error);
Expand All @@ -1418,13 +1447,13 @@ get_wrapper_shared_type_full (MonoType *t, gboolean is_field)
mono_class_set_skip_generic_constraints (klass);

t = m_class_get_byval_arg (klass);
MonoType *shared_type = get_wrapper_shared_vtype (t);
MonoType *shared_type = get_wrapper_shared_vtype (t, frame);
if (shared_type)
t = shared_type;
return t;
}
case MONO_TYPE_VALUETYPE: {
MonoType *shared_type = get_wrapper_shared_vtype (t);
MonoType *shared_type = get_wrapper_shared_vtype (t, frame);
if (shared_type)
t = shared_type;
return t;
Expand All @@ -1440,7 +1469,7 @@ get_wrapper_shared_type_full (MonoType *t, gboolean is_field)
static MonoType*
get_wrapper_shared_type (MonoType *t)
{
return get_wrapper_shared_type_full (t, FALSE);
return get_wrapper_shared_type_full (t, FALSE, NULL);
}

/* Returns the intptr type for types that are passed in a single register */
Expand Down
Loading