Skip to content

Optimize type check efficiency with .IsValueType #82305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 1 addition & 1 deletion src/libraries/Common/tests/System/Collections/IListTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1742,7 +1742,7 @@ protected override sealed bool ItemsMustBeUnique

protected override sealed bool ItemsMustBeNonNull
{
get { return default(T) != null; }
get { return typeof(T).IsValueType; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a test; it doesn't need to be changed.

}

protected override sealed object GenerateItem()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal static bool IsValueType<T>()
#if NETCOREAPP
return typeof(T).IsValueType;
#else
if (default(T) != null)
if (typeof(T).IsValueType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a downlevel path. This may be used in runtimes where typeof(T).IsValueType isn't an intrinsic and isn't optimized. Note that this is part of an #else where the #if NETCOREAPP already uses IsValueType.

{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public void Comparer_IComparerCompareWithObjectsNotOfMatchingTypeShouldThrow()
// throw if both inputs are non-null and one of them is not of type T
IComparer comparer = Comparer<T>.Default;
StrongBox<T> notOfTypeT = new StrongBox<T>(default(T));
if (default(T) != null) // if default(T) is null these asserts will fail as IComparer.Compare returns early if either side is null
if (typeof(T).IsValueType) // if default(T) is null these asserts will fail as IComparer.Compare returns early if either side is null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a test and doesn't need to be changed. (The comment would also be stale if it were changed.)

{
AssertExtensions.Throws<ArgumentException>(null, () => comparer.Compare(notOfTypeT, default(T))); // lhs is the problem
AssertExtensions.Throws<ArgumentException>(null, () => comparer.Compare(default(T), notOfTypeT)); // rhs is the problem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public void EqualityComparer_IEqualityComparerEqualsWithObjectsNotOfMatchingType
Assert.Equal(default(T) == null, comparer.Equals(null, default(T)));
Assert.True(comparer.Equals(null, null));

if (default(T) != null) // if default(T) is null this assert will fail as IEqualityComparer.Equals returns early if either input is null
if (typeof(T).IsValueType) // if default(T) is null this assert will fail as IEqualityComparer.Equals returns early if either input is null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a test and doesn't need to be changed.

{
AssertExtensions.Throws<ArgumentException>(null, () => comparer.Equals(notOfTypeT, default(T))); // lhs is the problem
AssertExtensions.Throws<ArgumentException>(null, () => comparer.Equals(default(T), notOfTypeT)); // rhs is the problem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ private static bool IsCompatibleObject(object? value)

private static void ValidateNullValue(object? value, string argument)
{
if (value == null && default(T) != null)
if (value == null && typeof(T).IsValueType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be wrong for Nullable<T>, e.g. if value is null and typeof(T) is typeof(int?), then the code previously wouldn't have thrown but now it will.

{
throw Error.InvalidNullValue(typeof(T), argument);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal static class AggregationMinMaxHelpers<T>

AssociativeAggregationOperator<T, Pair<bool, T>, T> aggregation =
new AssociativeAggregationOperator<T, Pair<bool, T>, T>(source, new Pair<bool, T>(false, default!), null,
true, intermediateReduce, finalReduce, resultSelector, default(T) != null, QueryAggregationOptions.AssociativeCommutative);
true, intermediateReduce, finalReduce, resultSelector, typeof(T).IsValueType, QueryAggregationOptions.AssociativeCommutative);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same Nullable<T> concern


return aggregation.Aggregate();
}
Expand Down Expand Up @@ -71,7 +71,7 @@ private static Func<Pair<bool, T>, T, Pair<bool, T>> MakeIntermediateReduceFunct
// the existing accumulated result is equal to the sign requested by the function factory,
// we will return a new pair that contains the current element as the best item. We will
// ignore null elements (for reference and nullable types) in the input stream.
if ((default(T) != null || element != null) &&
if ((typeof(T).IsValueType || element != null) &&
(!accumulator.First || Util.Sign(comparer.Compare(element, accumulator.Second)) == sign))
{
return new Pair<bool, T>(true, element);
Expand All @@ -98,7 +98,7 @@ private static Func<Pair<bool, T>, Pair<bool, T>, Pair<bool, T>> MakeFinalReduce
if (element.First &&
(!accumulator.First || Util.Sign(comparer.Compare(element.Second, accumulator.Second)) == sign))
{
Debug.Assert(default(T) != null || element.Second != null, "nulls unexpected in final reduce");
Debug.Assert(typeof(T).IsValueType || element.Second != null, "nulls unexpected in final reduce");
return new Pair<bool, T>(true, element.Second);
}

Expand Down
14 changes: 7 additions & 7 deletions src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public static unsafe bool Contains<T>(ref T searchSpace, T value, int length) wh

nint index = 0; // Use nint for arithmetic to avoid unnecessary 64->32->64 truncations

if (default(T) != null || (object?)value != null)
Copy link
Member

@stephentoub stephentoub Feb 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these changes really have a material impact on the codegen? I wouldn't expect them to. The before/after in the opening post where there's poorer code gen isn't for this case.

if (typeof(T).IsValueType || (object?)value != null)
{
Debug.Assert(value is not null);

Expand Down Expand Up @@ -303,7 +303,7 @@ public static unsafe int IndexOf<T>(ref T searchSpace, T value, int length) wher
Debug.Assert(length >= 0);

nint index = 0; // Use nint for arithmetic to avoid unnecessary 64->32->64 truncations
if (default(T) != null || (object?)value != null)
if (typeof(T).IsValueType || (object?)value != null)
{
Debug.Assert(value is not null);

Expand Down Expand Up @@ -393,7 +393,7 @@ public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, int lengt

T lookUp;
int index = 0;
if (default(T) != null || ((object?)value0 != null && (object?)value1 != null))
if (typeof(T).IsValueType || ((object?)value0 != null && (object?)value1 != null))
{
Debug.Assert(value0 is not null && value1 is not null);

Expand Down Expand Up @@ -499,7 +499,7 @@ public static int IndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2,

T lookUp;
int index = 0;
if (default(T) != null || ((object?)value0 != null && (object?)value1 != null && (object?)value2 != null))
if (typeof(T).IsValueType || ((object?)value0 != null && (object?)value1 != null && (object?)value2 != null))
{
Debug.Assert(value0 is not null && value1 is not null && value2 is not null);

Expand Down Expand Up @@ -713,7 +713,7 @@ public static int LastIndexOf<T>(ref T searchSpace, T value, int length) where T
{
Debug.Assert(length >= 0);

if (default(T) != null || (object?)value != null)
if (typeof(T).IsValueType || (object?)value != null)
{
Debug.Assert(value is not null);

Expand Down Expand Up @@ -797,7 +797,7 @@ public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, int l
Debug.Assert(length >= 0);

T lookUp;
if (default(T) != null || ((object?)value0 != null && (object?)value1 != null))
if (typeof(T).IsValueType || ((object?)value0 != null && (object?)value1 != null))
{
Debug.Assert(value0 is not null && value1 is not null);

Expand Down Expand Up @@ -902,7 +902,7 @@ public static int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, T val
Debug.Assert(length >= 0);

T lookUp;
if (default(T) != null || ((object?)value0 != null && (object?)value1 != null && (object?)value2 != null))
if (typeof(T).IsValueType || ((object?)value0 != null && (object?)value1 != null && (object?)value2 != null))
{
Debug.Assert(value0 is not null && value1 is not null && value2 is not null);

Expand Down