Skip to content
Draft
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 @@ -93,7 +93,10 @@ private partial bool TryHandleIntrinsic(
}
else
{
if (typeInstantiated.Instantiation.IsConstrainedToBeReferenceTypes())
bool hasReferenceTypeConstraints = typeInstantiated.Instantiation.IsConstrainedToBeReferenceTypes();
bool hasKnownReferenceTypeArguments = AreAllArgumentsKnownReferenceTypes(argumentValues[0]);

if (hasReferenceTypeConstraints || hasKnownReferenceTypeArguments)
{
// This will always succeed thanks to the runtime type loader
}
Expand Down Expand Up @@ -164,14 +167,20 @@ private partial bool TryHandleIntrinsic(
}
}
}
else if (methodInstantiated.Instantiation.IsConstrainedToBeReferenceTypes())
{
// This will always succeed thanks to the runtime type loader
}
else
{
// If the owning type is a generic definition, we can't help much.
triggersWarning = true;
bool hasReferenceTypeConstraints = methodInstantiated.Instantiation.IsConstrainedToBeReferenceTypes();
bool hasKnownReferenceTypeArguments = AreAllArgumentsKnownReferenceTypes(argumentValues[0]);

if (hasReferenceTypeConstraints || hasKnownReferenceTypeArguments)
{
// This will always succeed thanks to the runtime type loader
}
else
{
// If the owning type is a generic definition, we can't help much.
triggersWarning = true;
}
}
}
else if (methodValue == NullValue.Instance)
Expand Down Expand Up @@ -775,6 +784,48 @@ private sealed class MakeGenericTypeSite : INodeWithRuntimeDeterminedDependencie
}
}

private static bool AreAllArgumentsKnownReferenceTypes(in MultiValue typeArgumentsArray)
{
// Check if we can prove all type arguments will be reference types.
// This avoids false warnings when MakeGenericType is called with runtime checks like !t.IsValueType.

var typesValue = typeArgumentsArray.AsSingleValue();

// If we don't have an array value, we can't prove anything
if (typesValue is not ArrayValue array)
return false;

int? size = array.Size.AsConstInt();
if (size is null)
return false;

// Check each element in the array
for (int i = 0; i < size.Value; i++)
{
if (!array.TryGetValueByIndex(i, out MultiValue value))
return false;

var singleValue = value.AsSingleValue();

bool isKnownReferenceType = singleValue switch
{
// SystemTypeValue representing a specific type - check if it's not a value type
SystemTypeValue systemType => !systemType.RepresentedType.Type.IsValueType,
// GenericParameterValue - check if it has a reference type constraint (class constraint)
GenericParameterValue genericParam => genericParam.GenericParameter.GenericParameter.HasReferenceTypeConstraint,
// NullableSystemTypeValue is always a value type
NullableSystemTypeValue => false,
// Unknown value - can't prove it's a reference type
_ => false
};

if (!isKnownReferenceType)
return false;
}

return true;
}

}

file static class Extensions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Diagnostics.CodeAnalysis;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;

namespace Mono.Linker.Tests.Cases.RequiresCapability
{
[SkipKeptItemsValidation]
[ExpectedNoWarnings]
public class RequiresDynamicCodeForMakeGenericType
{
public static void Main()
{
TestWithReferenceTypeCheck();
TestWithValueTypeCheck();
TestWithUnknownType(typeof(int));
TestWithClassConstraint<string>();
TestWithStructConstraint<int>();
TestWithNoConstraint<string>();
}

class Generic<T>
{
}

// Should NOT warn - we know t is not a value type
static void TestWithReferenceTypeCheck()
{
Type t = typeof(string);
if (!t.IsValueType)
{
typeof(Generic<>).MakeGenericType(t);
}
}

// Should warn - we know t is a value type
[ExpectedWarning("IL3050", "MakeGenericType", Tool.NativeAot, "")]
static void TestWithValueTypeCheck()
{
Type t = typeof(int);
if (t.IsValueType)
{
typeof(Generic<>).MakeGenericType(t);
}
}

// Should warn - we don't know what type t is
[ExpectedWarning("IL3050", "MakeGenericType", Tool.NativeAot, "")]
static void TestWithUnknownType(Type t)
{
typeof(Generic<>).MakeGenericType(t);
}

// Should NOT warn - T has class constraint (reference type)
static void TestWithClassConstraint<T>() where T : class
{
typeof(Generic<>).MakeGenericType(typeof(T));
}

// Should warn - T has struct constraint (value type)
[ExpectedWarning("IL3050", "MakeGenericType", Tool.NativeAot, "")]
static void TestWithStructConstraint<T>() where T : struct
{
typeof(Generic<>).MakeGenericType(typeof(T));
}

// Should warn - T has no constraint
[ExpectedWarning("IL3050", "MakeGenericType", Tool.NativeAot, "")]
static void TestWithNoConstraint<T>()
{
typeof(Generic<>).MakeGenericType(typeof(T));
}
}
}
Loading