Skip to content
Open
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
27 changes: 26 additions & 1 deletion engine/Sandbox.Reflection/TypeLibrary/TypeDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,31 @@ public Type MakeGenericType( Type[] inargs )
if ( genericParams.Length != typeArgs.Length )
return null;


// Make a map from generic type parameters to type arguments
var substitutions = new Dictionary<Type, Type>();
for ( int i = 0; i < genericParams.Length; i++ )
{
var param = genericParams[i];
var arg = typeArgs[i];
substitutions.Add( param, arg );
}

// Substitutes generic type parameters in a type definition with type arguments
Type Substitute( Type type )
{
if ( !type.IsGenericType )
return type;
var genericDefinition = type.GetGenericTypeDefinition();
var genericParams = type.GenericTypeArguments;
for ( int i = 0; i < genericParams.Length; i++ )
{
if ( substitutions.TryGetValue( genericParams[i], out var substitutedType ) )
genericParams[i] = Substitute( substitutedType );
}
return genericDefinition.MakeGenericType( genericParams );
}

for ( int i = 0; i < genericParams.Length; i++ )
{
var param = genericParams[i];
Expand Down Expand Up @@ -691,7 +716,7 @@ public Type MakeGenericType( Type[] inargs )
var constraints = param.GetGenericParameterConstraints();
foreach ( var constraint in constraints )
{
if ( !constraint.IsAssignableFrom( arg ) )
if ( !Substitute( constraint ).IsAssignableFrom( arg ) )
return null;
}
}
Expand Down
62 changes: 62 additions & 0 deletions engine/Sandbox.Test.Unit/Reflection/TypeLibraryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,12 @@ public interface IConstraintBreaker<T>
void DoSomething( T value );
}

public interface IConstraintBreaker2<T, T2, T3>
{
void DoSomething( T value );
void DoSomething2( T2 value );
void DoSomething3( T3 value );
}


/// <summary>
Expand All @@ -360,6 +366,25 @@ public void ObeyGenericConstraints()
.CreateGeneric<IConstraintBreaker<TypeWrapper>>( [typeof( TypeWrapper )] );
Assert.IsNull( badboy );
}


/// <summary>
///
/// </summary>
[TestMethod]
public void ObeyRecursiveGenericConstraints()
{
var tl = new Sandbox.Internal.TypeLibrary();
tl.AddAssembly( ThisAssembly, true );

var goodboy = tl.GetType( typeof( ConstraintBreaker2<> ) )
.CreateGeneric<ConstraintBreaker2TypeErased>( [typeof( ConstraintBreaker2Inner )] );
Assert.IsNotNull( goodboy );

var badboy = tl.GetType( typeof( ConstraintBreaker2<> ) )
.CreateGeneric<ConstraintBreaker2TypeErased>( [typeof( TypeWrapper )] );
Assert.IsNull( badboy );
}
}

[Expose, MyType]
Expand All @@ -379,3 +404,40 @@ public void DoSomething( T value )

}
}


public class ConstraintBreaker2Inner :
IConstraintBreaker2<
ConstraintBreaker2Inner,
ConstraintBreaker2Inner,
ConstraintBreaker2Inner
>
{
public void DoSomething( ConstraintBreaker2Inner value )
{

}

public void DoSomething2( ConstraintBreaker2Inner value )
{

}

public void DoSomething3( ConstraintBreaker2Inner value )
{

}
}

public class ConstraintBreaker2TypeErased { }

[Expose]
public class ConstraintBreaker2<T> : ConstraintBreaker2TypeErased where T : IConstraintBreaker2<T, T, T>
{
public void DoSomething( T value )
{
value.DoSomething( value );
value.DoSomething2( value );
value.DoSomething3( value );
}
}