Skip to content

Commit

Permalink
Fix Type.ContainsGenericParameters for function pointers (dotnet#90864)
Browse files Browse the repository at this point in the history
* Fix Type.ContainsGenericParameters for function pointers

Fixes dotnet#84916

* Fix System.Reflection.MetadataLoadContext

* Fix Mono
  • Loading branch information
jkotas authored Aug 21, 2023
1 parent a2df89f commit af95bed
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 21 deletions.
40 changes: 40 additions & 0 deletions src/coreclr/vm/typedesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,31 @@ BOOL TypeDesc::IsSharedByGenericInstantiations()
return FALSE;
}

BOOL TypeDesc::ContainsGenericVariables(BOOL methodOnly)
{
if (IsGenericVariable())
{
if (!methodOnly)
return TRUE;

PTR_TypeVarTypeDesc pTyVar = dac_cast<PTR_TypeVarTypeDesc>(this);
return TypeFromToken(pTyVar->GetTypeOrMethodDef()) == mdtMethodDef;
}

if (HasTypeParam())
{
return GetRootTypeParam().ContainsGenericVariables(methodOnly);
}

if (IsFnPtr())
{
return dac_cast<PTR_FnPtrTypeDesc>(this)->ContainsGenericVariables(methodOnly);
}

return FALSE;
}


PTR_BaseDomain TypeDesc::GetDomain()
{
CONTRACTL
Expand Down Expand Up @@ -1670,6 +1695,21 @@ FnPtrTypeDesc::IsSharedByGenericInstantiations()
return FALSE;
} // FnPtrTypeDesc::IsSharedByGenericInstantiations

BOOL
FnPtrTypeDesc::ContainsGenericVariables(BOOL methodOnly)
{
LIMITED_METHOD_DAC_CONTRACT;

for (DWORD i = 0; i <= m_NumArgs; i++)
{
if (m_RetAndArgTypes[i].ContainsGenericVariables(methodOnly))
{
return TRUE;
}
}
return FALSE;
} // FnPtrTypeDesc::ContainsGenericVariables

#ifndef DACCESS_COMPILE

// Returns TRUE if all return and argument types are externally visible.
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/typedesc.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ class TypeDesc

BOOL IsSharedByGenericInstantiations();

BOOL ContainsGenericVariables(BOOL methodOnly);

protected:
// See methodtable.h for details of the flags with the same name there
enum
Expand Down Expand Up @@ -527,6 +529,8 @@ class FnPtrTypeDesc : public TypeDesc

BOOL IsSharedByGenericInstantiations();

BOOL ContainsGenericVariables(BOOL methodOnly);

#ifndef DACCESS_COMPILE
// Returns TRUE if all return and argument types are externally visible.
BOOL IsExternallyVisible() const;
Expand Down
24 changes: 4 additions & 20 deletions src/coreclr/vm/typehandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,26 +138,10 @@ BOOL TypeHandle::ContainsGenericVariables(BOOL methodOnly /*=FALSE*/) const
STATIC_CONTRACT_NOTHROW;
SUPPORTS_DAC;

if (HasTypeParam())
{
return GetTypeParam().ContainsGenericVariables(methodOnly);
}

if (IsGenericVariable())
{
if (!methodOnly)
return TRUE;

PTR_TypeVarTypeDesc pTyVar = dac_cast<PTR_TypeVarTypeDesc>(AsTypeDesc());
return TypeFromToken(pTyVar->GetTypeOrMethodDef()) == mdtMethodDef;
}
else if (HasInstantiation())
{
if (GetMethodTable()->ContainsGenericVariables(methodOnly))
return TRUE;
}

return FALSE;
if (IsTypeDesc())
return AsTypeDesc()->ContainsGenericVariables(methodOnly);
else
return AsMethodTable()->ContainsGenericVariables(methodOnly);
}

//@GENERICS:
Expand Down
19 changes: 19 additions & 0 deletions src/libraries/Common/tests/System/FunctionPointerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@ public static unsafe void RequiredModifiers()
Assert.Equal(typeof(Runtime.InteropServices.OutAttribute).Project(), parameters[1].GetRequiredCustomModifiers()[0]);
}

[Fact]
public static unsafe void GenericFunctionPointer()
{
Type t = typeof(FunctionPointerHolder).Project();

MethodInfo m1 = t.GetMethod(nameof(FunctionPointerHolder.GenericReturnValue), Bindings);
Type fcnPtr1 = m1.ReturnType;
Assert.True(fcnPtr1.IsFunctionPointer);
Assert.True(fcnPtr1.ContainsGenericParameters);

MethodInfo m2 = t.GetMethod(nameof(FunctionPointerHolder.GenericArgument), Bindings);
Type fcnPtr2 = m2.GetParameters()[1].ParameterType;
Assert.True(fcnPtr2.IsFunctionPointer);
Assert.True(fcnPtr2.ContainsGenericParameters);
}

[Theory]
[InlineData(nameof(FunctionPointerHolder.MethodReturnValue1),
"MethodReturnValue1()",
Expand Down Expand Up @@ -278,6 +294,9 @@ private unsafe class FunctionPointerHolder
public delegate* unmanaged[Stdcall, MemberFunction]<string, ref bool*, MyClass, in MyStruct, double> SeveralArguments() => default;
public delegate*<in int, out int, void> RequiredModifiers() => default;

public delegate*<T> GenericReturnValue<T>() => default;
public bool GenericArgument<T>(int x, delegate*<T[], void> fptr) => default;

public class MyClass { }
public struct MyStruct { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,23 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj)
public sealed override bool IsGenericParameter => false;
public sealed override bool IsGenericTypeParameter => false;
public sealed override bool IsGenericMethodParameter => false;
public sealed override bool ContainsGenericParameters => IsGenericTypeDefinition;

public sealed override bool ContainsGenericParameters
{
get
{
if (_returnType.ContainsGenericParameters)
return true;

foreach (Type parameterType in _parameterTypes)
{
if (parameterType.ContainsGenericParameters)
return true;
}

return false;
}
}

protected sealed override TypeCode GetTypeCodeImpl() => TypeCode.Object;

Expand Down
10 changes: 10 additions & 0 deletions src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2048,6 +2048,16 @@ public override bool ContainsGenericParameters
if (HasElementType)
return GetElementType().ContainsGenericParameters;

if (IsFunctionPointer)
{
if (GetFunctionPointerReturnType().ContainsGenericParameters)
return true;

foreach (Type arg in GetFunctionPointerParameterTypes())
if (arg.ContainsGenericParameters)
return true;
}

return false;
}
}
Expand Down

0 comments on commit af95bed

Please sign in to comment.