summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Robinson <arobins@microsoft.com>2024-03-01 19:53:48 -0800
committerGitHub <noreply@github.com>2024-03-01 19:53:48 -0800
commit2ab51c6ef3fb24a625c1f4729f9b2e626a0f141b (patch)
treed7bd2633bdbc0e8c5647083d9c7e91ec86f310b3
parent6fa046837910fcc7a7dbd64be4b1ad98f9ea5db3 (diff)
CoreCLR NativeAOT - ByRef generics and ByRef implementing interfaces (#98070)
* ByRefLike generic/interface follow-up work * Create negative tests * Apply API review feedback. Add FeatureFlag.
-rw-r--r--docs/design/features/byreflike-generics.md126
-rw-r--r--src/coreclr/ilasm/asmparse.y2
-rw-r--r--src/coreclr/ilasm/prebuilt/asmparse.cpp2
-rw-r--r--src/coreclr/ildasm/dasm.cpp2
-rw-r--r--src/coreclr/inc/corhdr.h2
-rw-r--r--src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidator.cs10
-rw-r--r--src/coreclr/tools/Common/TypeSystem/Common/GenericParameterDesc.cs8
-rw-r--r--src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs2
-rw-r--r--src/coreclr/tools/Common/TypeSystem/Ecma/EcmaGenericParameter.cs2
-rw-r--r--src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeGenericInfoMapNode.cs2
-rw-r--r--src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeValidationChecker.cs7
-rw-r--r--src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/Platform.cs1
-rw-r--r--src/coreclr/vm/siginfo.cpp6
-rw-r--r--src/coreclr/vm/typedesc.cpp2
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/Reflection/GenericParameterAttributes.cs1
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.cs6
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs4
-rw-r--r--src/libraries/System.Runtime/ref/System.Runtime.cs2
-rw-r--r--src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs4
-rw-r--r--src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_brl.il153
-rw-r--r--src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_brl.ilproj5
-rw-r--r--src/tests/Loader/classloader/generics/ByRefLike/GenericTypeSubstitution.cs12
-rw-r--r--src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il29
-rw-r--r--src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharpNegative.il143
-rw-r--r--src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharpNegative.ilproj10
-rw-r--r--src/tests/Loader/classloader/generics/ByRefLike/ValidateNegative.cs25
-rw-r--r--src/tests/Loader/classloader/generics/ByRefLike/ValidateNegative.csproj18
27 files changed, 528 insertions, 58 deletions
diff --git a/docs/design/features/byreflike-generics.md b/docs/design/features/byreflike-generics.md
index d644a25e7f3f..99aa905ebf0f 100644
--- a/docs/design/features/byreflike-generics.md
+++ b/docs/design/features/byreflike-generics.md
@@ -127,3 +127,129 @@ The following are IL sequences involving the `box` instruction. They are used fo
`box` ; `isinst` ; `unbox.any` &ndash; The box, `isint`, and unbox target types are all equal.
`box` ; `isinst` ; `br_true/false` &ndash; The box target type is equal to the unboxed target type or the box target type is `Nullable<T>` and target type equalities can be computed.
+
+## Examples
+
+Below are valid and invalid examples of ByRefLike as Generic parameters. All examples use the **not official** syntax, `allows ref struct`, for indicating the Generic permits ByRefLike types.
+
+**1) Valid**
+```csharp
+class A<T1> where T1: allows ref struct
+{
+ public void M();
+}
+
+// The derived class is okay to lack the 'allows'
+// because the base permits non-ByRefLike (default)
+// _and_ ByRefLike types.
+class B<T2> : A<T2>
+{
+ public void N()
+ => M(); // Any T2 satisfies the constraints from A<>
+}
+```
+
+**2) Invalid**
+```csharp
+class A<T1>
+{
+ public void M();
+}
+
+// The derived class cannot push up the allows
+// constraint for ByRefLike types.
+class B<T2> : A<T2> where T2: allows ref struct
+{
+ public void N()
+ => M(); // A<> may not permit a T2
+}
+```
+
+**3) Valid**
+```csharp
+interface IA
+{
+ void M();
+}
+
+ref struct A : IA
+{
+ public void M() { }
+}
+
+class B
+{
+ // This call is permitted because no boxing is needed
+ // to dispatch to the method - it is implemented on A.
+ public static void C<T>(T t) where T: IA, allows ref struct
+ => t.M();
+}
+```
+
+**4) Invalid**
+```csharp
+interface IA
+{
+ public void M() { }
+}
+
+ref struct A : IA
+{
+ // Relies on IA::M() implementation.
+}
+
+class B
+{
+ // Reliance on a DIM forces the generic parameter
+ // to be boxed, which is invalid for ByRefLike types.
+ public static void C<T>(T t) where T: IA, allows ref struct
+ => t.M();
+}
+```
+
+**5) Valid**
+```csharp
+class A<T1> where T1: allows ref struct
+{
+}
+
+class B<T2>
+{
+ // The type parameter is okay to lack the 'allows'
+ // because the field permits non-ByRefLike (default)
+ // _and_ ByRefLike types.
+ A<T2> Field;
+}
+```
+
+**6) Invalid**
+```csharp
+class A<T1>
+{
+}
+
+class B<T2> where T2: allows ref struct
+{
+ // The type parameter can be passed to
+ // the field type, but will fail if
+ // T2 is a ByRefLike type.
+ A<T2> Field;
+}
+```
+
+**7) Invalid**
+```csharp
+class A
+{
+ virtual void M<T1>() where T1: allows ref struct;
+}
+
+class B : A
+{
+ // Override methods need to match be at least
+ // as restrictive with respect to constraints.
+ // If a user has an instance of A, they are
+ // not aware they could be calling B.
+ override void M<T2>();
+}
+``` \ No newline at end of file
diff --git a/src/coreclr/ilasm/asmparse.y b/src/coreclr/ilasm/asmparse.y
index 73ef9a892b5e..c9861d58d797 100644
--- a/src/coreclr/ilasm/asmparse.y
+++ b/src/coreclr/ilasm/asmparse.y
@@ -486,7 +486,7 @@ typarAttrib : '+' { $$ = gpCovariant;
| '-' { $$ = gpContravariant; }
| CLASS_ { $$ = gpReferenceTypeConstraint; }
| VALUETYPE_ { $$ = gpNotNullableValueTypeConstraint; }
- | BYREFLIKE_ { $$ = gpAcceptByRefLike; }
+ | BYREFLIKE_ { $$ = gpAllowByRefLike; }
| _CTOR { $$ = gpDefaultConstructorConstraint; }
| FLAGS_ '(' int32 ')' { $$ = (CorGenericParamAttr)$3; }
;
diff --git a/src/coreclr/ilasm/prebuilt/asmparse.cpp b/src/coreclr/ilasm/prebuilt/asmparse.cpp
index 6bf91f56c57f..08f686f29018 100644
--- a/src/coreclr/ilasm/prebuilt/asmparse.cpp
+++ b/src/coreclr/ilasm/prebuilt/asmparse.cpp
@@ -2523,7 +2523,7 @@ case 152:
{ yyval.int32 = gpNotNullableValueTypeConstraint; } break;
case 153:
#line 489 "asmparse.y"
-{ yyval.int32 = gpAcceptByRefLike; } break;
+{ yyval.int32 = gpAllowByRefLike; } break;
case 154:
#line 490 "asmparse.y"
{ yyval.int32 = gpDefaultConstructorConstraint; } break;
diff --git a/src/coreclr/ildasm/dasm.cpp b/src/coreclr/ildasm/dasm.cpp
index be95e36fa5d5..21dff99a3812 100644
--- a/src/coreclr/ildasm/dasm.cpp
+++ b/src/coreclr/ildasm/dasm.cpp
@@ -3081,7 +3081,7 @@ char *DumpGenericPars(_Inout_updates_(SZSTRING_SIZE) char* szString, mdToken tok
if ((attr & gpNotNullableValueTypeConstraint) != 0)
szptr += sprintf_s(szptr,SZSTRING_REMAINING_SIZE(szptr), "valuetype ");
CHECK_REMAINING_SIZE;
- if ((attr & gpAcceptByRefLike) != 0)
+ if ((attr & gpAllowByRefLike) != 0)
szptr += sprintf_s(szptr,SZSTRING_REMAINING_SIZE(szptr), "byreflike ");
CHECK_REMAINING_SIZE;
if ((attr & gpDefaultConstructorConstraint) != 0)
diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h
index 3f67b33da916..c12c1cfdd4f7 100644
--- a/src/coreclr/inc/corhdr.h
+++ b/src/coreclr/inc/corhdr.h
@@ -847,7 +847,7 @@ typedef enum CorGenericParamAttr
gpReferenceTypeConstraint = 0x0004, // type argument must be a reference type
gpNotNullableValueTypeConstraint = 0x0008, // type argument must be a value type but not Nullable
gpDefaultConstructorConstraint = 0x0010, // type argument must have a public default constructor
- gpAcceptByRefLike = 0x0020, // type argument can be ByRefLike
+ gpAllowByRefLike = 0x0020, // type argument can be ByRefLike
} CorGenericParamAttr;
// structures and enums moved from COR.H
diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidator.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidator.cs
index ccb1a9279f0a..d347b6d0f4af 100644
--- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidator.cs
+++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/TypeLoader/ConstraintValidator.cs
@@ -13,9 +13,9 @@ namespace Internal.Reflection.Execution
{
private static bool SatisfiesConstraints(this Type genericVariable, SigTypeContext typeContextOfConstraintDeclarer, Type typeArg)
{
- GenericParameterAttributes specialConstraints = genericVariable.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
+ GenericParameterAttributes attributes = genericVariable.GenericParameterAttributes;
- if ((specialConstraints & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
+ if ((attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
{
if (!typeArg.IsValueType)
{
@@ -30,19 +30,19 @@ namespace Internal.Reflection.Execution
}
}
- if ((specialConstraints & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
+ if ((attributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
{
if (typeArg.IsValueType)
return false;
}
- if ((specialConstraints & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
+ if ((attributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
{
if (!typeArg.HasExplicitOrImplicitPublicDefaultConstructor())
return false;
}
- if (typeArg.IsByRefLike && (specialConstraints & (GenericParameterAttributes)0x20 /* GenericParameterAttributes.AcceptByRefLike */) == 0)
+ if (typeArg.IsByRefLike && (attributes & (GenericParameterAttributes)0x20 /* GenericParameterAttributes.AllowByRefLike */) == 0)
return false;
// Now check general subtype constraints
diff --git a/src/coreclr/tools/Common/TypeSystem/Common/GenericParameterDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/GenericParameterDesc.cs
index e1bf789b1123..c361733737d4 100644
--- a/src/coreclr/tools/Common/TypeSystem/Common/GenericParameterDesc.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Common/GenericParameterDesc.cs
@@ -61,7 +61,7 @@ namespace Internal.TypeSystem
/// <summary>
/// A type is permitted to be ByRefLike.
/// </summary>
- AcceptByRefLike = 0x20,
+ AllowByRefLike = 0x20,
}
public abstract partial class GenericParameterDesc : TypeDesc
@@ -159,13 +159,13 @@ namespace Internal.TypeSystem
}
/// <summary>
- /// Does this generic parameter have the AcceptByRefLike flag
+ /// Does this generic parameter have the AllowByRefLike flag
/// </summary>
- public bool HasAcceptByRefLikeConstraint
+ public bool HasAllowByRefLikeConstraint
{
get
{
- return (Constraints & GenericConstraints.AcceptByRefLike) != 0;
+ return (Constraints & GenericConstraints.AllowByRefLike) != 0;
}
}
diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs
index acd8e291cebd..97bceb5b1b83 100644
--- a/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs
@@ -50,7 +50,7 @@ namespace Internal.TypeSystem
}
// Check for ByRefLike support
- if (instantiationParam.IsByRefLike && (constraints & GenericConstraints.AcceptByRefLike) == 0)
+ if (instantiationParam.IsByRefLike && (constraints & GenericConstraints.AllowByRefLike) == 0)
return false;
var instantiatedConstraints = default(ArrayBuilder<TypeDesc>);
diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaGenericParameter.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaGenericParameter.cs
index ac6cd4307ca7..8ffa09bc12eb 100644
--- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaGenericParameter.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaGenericParameter.cs
@@ -113,7 +113,7 @@ namespace Internal.TypeSystem.Ecma
{
Debug.Assert((int)GenericConstraints.DefaultConstructorConstraint == (int)GenericParameterAttributes.DefaultConstructorConstraint);
GenericParameter parameter = _module.MetadataReader.GetGenericParameter(_handle);
- const GenericParameterAttributes mask = GenericParameterAttributes.SpecialConstraintMask | (GenericParameterAttributes)GenericConstraints.AcceptByRefLike;
+ const GenericParameterAttributes mask = GenericParameterAttributes.SpecialConstraintMask | (GenericParameterAttributes)GenericConstraints.AllowByRefLike;
return (GenericConstraints)(parameter.Attributes & mask);
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeGenericInfoMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeGenericInfoMapNode.cs
index e6b4762433df..8905c2aa31d7 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeGenericInfoMapNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeGenericInfoMapNode.cs
@@ -59,7 +59,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
if ((genericParameter.Attributes & GenericParameterAttributes.VarianceMask) != GenericParameterAttributes.None)
hasVariance = true;
- if ((genericParameter.Attributes & (GenericParameterAttributes.SpecialConstraintMask | (GenericParameterAttributes)GenericConstraints.AcceptByRefLike)) != default(GenericParameterAttributes) ||
+ if ((genericParameter.Attributes & (GenericParameterAttributes.SpecialConstraintMask | (GenericParameterAttributes)GenericConstraints.AllowByRefLike)) != default(GenericParameterAttributes) ||
(genericParameter.GetConstraints().Count > 0))
{
hasConstraints = true;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeValidationChecker.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeValidationChecker.cs
index 88882e12f1f5..a257dbbe967f 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeValidationChecker.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeValidationChecker.cs
@@ -107,7 +107,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
// The runtime has a number of checks in the type loader which it will skip running if the SkipValidation flag is set
// This function attempts to document all of them, and implement *some* of them.
- // This function performs a portion of the validation skipping that has been found to have some importance, or to serve as
+ // This function performs a portion of the validation skipping that has been found to have some importance, or to serve as
// In addition, there are comments about all validation skipping activities that the runtime will perform.
try
{
@@ -488,8 +488,9 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
if (!parameterOfDecl.HasReferenceTypeConstraint)
return false;
- if (parameterOfDecl.HasAcceptByRefLikeConstraint)
- if (!parameterOfImpl.HasAcceptByRefLikeConstraint)
+ // Constraints that 'allow' must check the impl first
+ if (parameterOfImpl.HasAllowByRefLikeConstraint)
+ if (!parameterOfDecl.HasAllowByRefLikeConstraint)
return false;
HashSet<TypeDesc> constraintsOnDecl = new HashSet<TypeDesc>();
diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/Platform.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/Platform.cs
index fda0f02f5630..a2c1a1e06ad6 100644
--- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/Platform.cs
+++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/Platform.cs
@@ -244,6 +244,7 @@ namespace System.Runtime.CompilerServices
public static class RuntimeFeature
{
public const string ByRefFields = nameof(ByRefFields);
+ public const string ByRefLikeGenerics = nameof(ByRefLikeGenerics);
public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention);
public const string VirtualStaticsInInterfaces = nameof(VirtualStaticsInInterfaces);
}
diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp
index 115b5454bf59..531f6d95f686 100644
--- a/src/coreclr/vm/siginfo.cpp
+++ b/src/coreclr/vm/siginfo.cpp
@@ -4813,9 +4813,11 @@ BOOL MetaSig::CompareVariableConstraints(const Substitution *pSubst1,
if ((specialConstraints2 & (gpDefaultConstructorConstraint | gpNotNullableValueTypeConstraint)) == 0)
return FALSE;
}
- if ((specialConstraints1 & gpAcceptByRefLike) != 0)
+
+ // Constraints that 'allow' must check the overridden first
+ if ((specialConstraints2 & gpAllowByRefLike) != 0)
{
- if ((specialConstraints2 & gpAcceptByRefLike) == 0)
+ if ((specialConstraints1 & gpAllowByRefLike) == 0)
return FALSE;
}
}
diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp
index ccb342ca0358..72bc9e50c29b 100644
--- a/src/coreclr/vm/typedesc.cpp
+++ b/src/coreclr/vm/typedesc.cpp
@@ -1497,7 +1497,7 @@ BOOL TypeVarTypeDesc::SatisfiesConstraints(SigTypeContext *pTypeContextOfConstra
return FALSE;
}
- if (thArg.IsByRefLike() && (specialConstraints & gpAcceptByRefLike) == 0)
+ if (thArg.IsByRefLike() && (specialConstraints & gpAllowByRefLike) == 0)
return FALSE;
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/GenericParameterAttributes.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/GenericParameterAttributes.cs
index c05d0ec6f200..26bb5d929a4f 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Reflection/GenericParameterAttributes.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/GenericParameterAttributes.cs
@@ -14,5 +14,6 @@ namespace System.Reflection
ReferenceTypeConstraint = 0x0004,
NotNullableValueTypeConstraint = 0x0008,
DefaultConstructorConstraint = 0x0010,
+ AllowByRefLike = 0x0020,
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.cs
index e69436494251..392a4902337a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.cs
@@ -33,6 +33,11 @@ namespace System.Runtime.CompilerServices
public const string ByRefFields = nameof(ByRefFields);
/// <summary>
+ /// Represents a runtime feature where byref-like types can be used in Generic parameters.
+ /// </summary>
+ public const string ByRefLikeGenerics = nameof(ByRefLikeGenerics);
+
+ /// <summary>
/// Indicates that this version of runtime supports virtual static members of interfaces.
/// </summary>
public const string VirtualStaticsInInterfaces = nameof(VirtualStaticsInInterfaces);
@@ -52,6 +57,7 @@ namespace System.Runtime.CompilerServices
case PortablePdb:
case CovariantReturnsOfClasses:
case ByRefFields:
+ case ByRefLikeGenerics:
case UnmanagedSignatureCallingConvention:
case DefaultImplementationsOfInterfaces:
case VirtualStaticsInInterfaces:
diff --git a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs
index bd6a6f89bb13..17fbb2950c6c 100644
--- a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs
@@ -692,7 +692,7 @@ namespace System
if (constraint.IsGenericParameter)
{
- GenericParameterAttributes special = constraint.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
+ GenericParameterAttributes special = constraint.GenericParameterAttributes;
if ((special & GenericParameterAttributes.ReferenceTypeConstraint) == 0 &&
(special & GenericParameterAttributes.NotNullableValueTypeConstraint) == 0)
@@ -704,7 +704,7 @@ namespace System
if (baseType == ObjectType)
{
- GenericParameterAttributes special = GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
+ GenericParameterAttributes special = GenericParameterAttributes;
if ((special & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
baseType = ValueType;
}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index b787a2af8c68..68b91e6794c9 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -11599,6 +11599,7 @@ namespace System.Reflection
NotNullableValueTypeConstraint = 8,
DefaultConstructorConstraint = 16,
SpecialConstraintMask = 28,
+ AllowByRefLike = 32,
}
public partial interface ICustomAttributeProvider
{
@@ -13113,6 +13114,7 @@ namespace System.Runtime.CompilerServices
public static partial class RuntimeFeature
{
public const string ByRefFields = "ByRefFields";
+ public const string ByRefLikeGenerics = "ByRefLikeGenerics";
public const string CovariantReturnsOfClasses = "CovariantReturnsOfClasses";
public const string DefaultImplementationsOfInterfaces = "DefaultImplementationsOfInterfaces";
public const string NumericIntPtr = "NumericIntPtr";
diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
index 50a7b0943e59..765ebd4ad5be 100644
--- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
+++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
@@ -806,6 +806,10 @@ public sealed class XUnitWrapperGenerator : IIncrementalGenerator
// If we're building tests not for Mono, we can skip handling the specifics of the SkipOnMonoAttribute.
continue;
}
+ if (filterAttribute.ConstructorArguments.Length <= 1)
+ {
+ return ImmutableArray<ITestInfo>.Empty;
+ }
testInfos = DecorateWithSkipOnPlatform(testInfos, (int)filterAttribute.ConstructorArguments[1].Value!, options);
break;
case "Xunit.SkipOnPlatformAttribute":
diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_brl.il b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_brl.il
new file mode 100644
index 000000000000..9382ff66730c
--- /dev/null
+++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_brl.il
@@ -0,0 +1,153 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+.assembly extern System.Console { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) }
+.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) }
+.assembly extern Microsoft.DotNet.XUnitExtensions {}
+.assembly extern xunit.core {}
+.assembly constrained2_brl { }
+
+.class interface private auto ansi abstract
+ IAdder
+{
+ .method public hidebysig newslot virtual
+ instance int32 Add(int32) cil managed
+ {
+ ldstr "Calling DIM from ByRefLike type is invalid"
+ newobj instance void [System.Runtime]System.Exception::.ctor(string)
+ throw
+ }
+}
+
+.class private sequential ansi sealed beforefieldinit Adder
+ extends [System.Runtime]System.ValueType
+ implements IAdder
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (
+ 01 00 00 00
+ )
+
+ .field private int32 _field
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor (int32) cil managed
+ {
+ ldarg.0
+ ldarg.1
+ stfld int32 Adder::_field
+ ret
+ }
+
+ .method public hidebysig newslot virtual
+ instance int32 Add(int32) cil managed
+ {
+ // Load field and add with argument
+ ldarg.0
+ dup
+ ldfld int32 valuetype Adder::_field
+ ldarg.1
+ add
+
+ // Update the field
+ stfld int32 valuetype Adder::_field
+
+ // Return the field value
+ ldarg.0
+ ldfld int32 valuetype Adder::_field
+ ret
+ }
+}
+
+.class private sequential ansi sealed beforefieldinit Adder_Invalid
+ extends [System.Runtime]System.ValueType
+ implements IAdder
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (
+ 01 00 00 00
+ )
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor (int32) cil managed
+ {
+ ret
+ }
+
+ //
+ // Deferring to the DIM on IAdder
+ //
+}
+
+.method public hidebysig static int32 Check<byreflike (IAdder) T>(!!0, int32)
+{
+ ldarga.s 0
+ ldarg.1
+ constrained. !!0
+ callvirt instance int32 IAdder::Add(int32)
+ ret
+}
+
+.class public auto ansi abstract sealed beforefieldinit constrained2_brl
+ extends [System.Runtime]System.Object
+{
+ .method public hidebysig static int32 Main()
+ {
+ .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = (
+ 01 00 00 00
+ )
+ .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.SkipOnMonoAttribute::.ctor(string, valuetype [Microsoft.DotNet.XUnitExtensions]Xunit.TestPlatforms) = (
+ 01 00 2c 4d 6f 6e 6f 20 64 6f 65 73 20 6e 6f 74
+ 20 73 75 70 70 6f 72 74 20 42 79 52 65 66 4c 69
+ 6b 65 20 67 65 6e 65 72 69 63 73 20 79 65 74 ff
+ ff ff ff 00 00
+ )
+ .entrypoint
+
+ .locals init (
+ valuetype Adder,
+ valuetype Adder_Invalid
+ )
+
+ // Initialize Adder instance
+ ldloca.s 0
+ ldc.i4 10
+ call instance void Adder::.ctor(int32)
+
+ ldstr "Validate constrained call of ByRefLike interface method passes"
+ call void [System.Console]System.Console::WriteLine(string)
+ ldloc.0
+ ldc.i4 20
+ call int32 Check<valuetype Adder>(!!0, int32)
+ ldc.i4 30
+ ceq
+ brfalse FAIL
+
+ // Initialize Adder_Invalid instance
+ ldloca.s 1
+ ldc.i4 10
+ call instance void Adder_Invalid::.ctor(int32)
+
+ .try
+ {
+ ldstr "Validate constrained call of ByRefLike interface DIM fails"
+ call void [System.Console]System.Console::WriteLine(string)
+
+ ldloc.1
+ ldc.i4 20
+ call int32 Check<valuetype Adder_Invalid>(!!0, int32)
+ leave FAIL
+ }
+ catch [System.Runtime]System.Exception
+ {
+ pop
+ leave ExpectedFailure
+ }
+
+ ExpectedFailure:
+ ldc.i4 100
+ ret
+
+ FAIL:
+ ldc.i4 101
+ ret
+ }
+}
diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_brl.ilproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_brl.ilproj
new file mode 100644
index 000000000000..8aceddffe8f5
--- /dev/null
+++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_brl.ilproj
@@ -0,0 +1,5 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+ <ItemGroup>
+ <Compile Include="constrained2_brl.il" />
+ </ItemGroup>
+</Project>
diff --git a/src/tests/Loader/classloader/generics/ByRefLike/GenericTypeSubstitution.cs b/src/tests/Loader/classloader/generics/ByRefLike/GenericTypeSubstitution.cs
index 3453b2a1d3e5..b35e59576abb 100644
--- a/src/tests/Loader/classloader/generics/ByRefLike/GenericTypeSubstitution.cs
+++ b/src/tests/Loader/classloader/generics/ByRefLike/GenericTypeSubstitution.cs
@@ -16,7 +16,7 @@ public class GenericTypeSubstitution
public static void AllowByRefLike_Substituted_For_AllowByRefLike()
{
Console.WriteLine($"{nameof(AllowByRefLike_Substituted_For_AllowByRefLike)}...");
-
+
Console.WriteLine($" -- Instantiate: {Exec.TypeSubstitutionInterfaceImplementationAllowByRefLike()}");
Console.WriteLine($" -- Instantiate: {Exec.TypeSubstitutionInheritanceAllowByRefLike()}");
Console.WriteLine($" -- Instantiate: {Exec.TypeSubstitutionFieldAllowByRefLike()}");
@@ -26,18 +26,18 @@ public class GenericTypeSubstitution
[SkipOnMono("Mono does not support ByRefLike generics yet")]
public static void NonByRefLike_Substituted_For_AllowByRefLike()
{
+ Console.WriteLine($"{nameof(NonByRefLike_Substituted_For_AllowByRefLike)}...");
+
Console.WriteLine($" -- Instantiate: {Exec.TypeSubstitutionInterfaceImplementationNonByRefLike()}");
Console.WriteLine($" -- Instantiate: {Exec.TypeSubstitutionInheritanceNonByRefLike()}");
Console.WriteLine($" -- Instantiate: {Exec.TypeSubstitutionFieldNonByRefLike()}");
}
[Fact]
- [ActiveIssue("To be created", TestRuntimes.CoreCLR)]
[SkipOnMono("Mono does not support ByRefLike generics yet")]
- public static void AllowByRefLike_Substituted_For_NonByRefLike_Invalid()
+ public static void AllowByRefLike_Substituted_For_NonByRefLike()
{
- Assert.Throws<TypeLoadException>(() => { Exec.TypeSubstitutionInterfaceImplementationAllowByRefLikeIntoNonByRefLike(); });
- Assert.Throws<TypeLoadException>(() => { Exec.TypeSubstitutionInheritanceAllowByRefLikeIntoNonByRefLike(); });
- Assert.Throws<TypeLoadException>(() => { Exec.TypeSubstitutionFieldAllowByRefLikeIntoNonByRefLike(); });
+ Console.WriteLine($"{nameof(AllowByRefLike_Substituted_For_NonByRefLike)}...");
+ Exec.TypeSubstitutionFieldAllowNonByRefLikeIntoNonByRefLike();
}
} \ No newline at end of file
diff --git a/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il b/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il
index d81452d398af..8deff773fb24 100644
--- a/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il
+++ b/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharp.il
@@ -439,15 +439,6 @@
}
// Invalid generic substitution of non-allow-byreflike with allow-byreflike
-.class interface public auto ansi abstract InvalidCSharp.GenericDerivedInterface_Invalid`1<byreflike T>
- implements class InvalidCSharp.GenericInterface_Invalid`1<!T>
-{
-}
-
-.class public sequential ansi sealed beforefieldinit InvalidCSharp.GenericDerivedClass_Invalid`1<byreflike T>
- extends class InvalidCSharp.GenericClass_Invalid`1<!T>
-{
-}
.class public sequential ansi sealed beforefieldinit InvalidCSharp.GenericValueTypeWrapper_Invalid`1<byreflike T>
extends [System.Runtime]System.ValueType
@@ -798,27 +789,9 @@
callvirt instance string [System.Runtime]System.Object::ToString()
ret
}
-
- .method public hidebysig static
- string TypeSubstitutionInterfaceImplementationAllowByRefLikeIntoNonByRefLike() cil managed
- {
- ldtoken class InvalidCSharp.GenericDerivedInterface_Invalid`1<valuetype RegularValueType>
- call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
- callvirt instance string [System.Runtime]System.Object::ToString()
- ret
- }
-
- .method public hidebysig static
- string TypeSubstitutionInheritanceAllowByRefLikeIntoNonByRefLike() cil managed
- {
- ldtoken class InvalidCSharp.GenericDerivedClass_Invalid`1<valuetype RegularValueType>
- call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
- callvirt instance string [System.Runtime]System.Object::ToString()
- ret
- }
.method public hidebysig static
- string TypeSubstitutionFieldAllowByRefLikeIntoNonByRefLike() cil managed
+ string TypeSubstitutionFieldAllowNonByRefLikeIntoNonByRefLike() cil managed
{
ldtoken valuetype InvalidCSharp.GenericValueTypeWrapper_Invalid`1<valuetype RegularValueType>
call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
diff --git a/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharpNegative.il b/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharpNegative.il
new file mode 100644
index 000000000000..08767e372ea4
--- /dev/null
+++ b/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharpNegative.il
@@ -0,0 +1,143 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+.assembly extern System.Console { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) }
+.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) }
+
+.assembly InvalidCSharpNegative { }
+
+.class public sequential ansi sealed beforefieldinit ByRefLikeType
+ extends [System.Runtime]System.ValueType
+{
+ .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = (
+ 01 00 00 00
+ )
+}
+
+//
+// Begin invalid
+//
+
+.class public sequential ansi sealed beforefieldinit InvalidCSharpNegative.GenericClass_Invalid`1<T>
+ extends [System.Runtime]System.Object
+{
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
+
+.class interface public auto ansi abstract InvalidCSharpNegative.GenericInterface_Invalid`1<T>
+{
+}
+
+.class public sequential ansi sealed beforefieldinit InvalidCSharpNegative.GenericValueType_Invalid`1<T>
+ extends [System.Runtime]System.ValueType
+{
+}
+
+// Invalid generic substitution of non-allow-byreflike with allow-byreflike
+.class interface public auto ansi abstract InvalidCSharpNegative.GenericDerivedInterface_Invalid`1<byreflike T>
+ implements class InvalidCSharpNegative.GenericInterface_Invalid`1<!T>
+{
+}
+
+.class public sequential ansi sealed beforefieldinit InvalidCSharpNegative.GenericDerivedClass_Invalid`1<byreflike T>
+ extends class InvalidCSharpNegative.GenericClass_Invalid`1<!T>
+{
+}
+
+.class public sequential ansi sealed beforefieldinit InvalidCSharpNegative.GenericValueTypeWrapper_Invalid`1<byreflike T>
+ extends [System.Runtime]System.ValueType
+{
+ .field public valuetype InvalidCSharpNegative.GenericValueType_Invalid`1<!T> fld;
+}
+
+.class public auto ansi beforefieldinit InvalidCSharpNegative.BaseClassWithGenericMethod
+ extends [System.Runtime]System.Object
+{
+ .method public hidebysig newslot virtual
+ instance void AcceptsByRefLike<byreflike T> () cil managed
+ {
+ ret
+ }
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
+
+.class public auto ansi beforefieldinit InvalidCSharpNegative.DerivedClassWithGenericMethod_Invalid
+ extends InvalidCSharpNegative.BaseClassWithGenericMethod
+{
+ .method public hidebysig static
+ class InvalidCSharpNegative.BaseClassWithGenericMethod Create () cil managed noinlining
+ {
+ newobj instance void InvalidCSharpNegative.DerivedClassWithGenericMethod_Invalid::.ctor()
+ ret
+ }
+ .method public hidebysig virtual
+ instance void AcceptsByRefLike<T> () cil managed // Missing constraint
+ {
+ ret
+ }
+ .method private hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ ldarg.0
+ call instance void InvalidCSharpNegative.BaseClassWithGenericMethod::.ctor()
+ ret
+ }
+}
+
+// Entry points
+
+.class public auto ansi abstract sealed beforefieldinit Exec
+ extends [System.Runtime]System.Object
+{
+ .method public hidebysig static
+ string TypeSubstitutionInterfaceImplementationAllowByRefLikeIntoNonByRefLike() cil managed
+ {
+ ldtoken class InvalidCSharpNegative.GenericDerivedInterface_Invalid`1<valuetype ByRefLikeType>
+ call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ callvirt instance string [System.Runtime]System.Object::ToString()
+ ret
+ }
+
+ .method public hidebysig static
+ string TypeSubstitutionInheritanceAllowByRefLikeIntoNonByRefLike() cil managed
+ {
+ ldtoken class InvalidCSharpNegative.GenericDerivedClass_Invalid`1<valuetype ByRefLikeType>
+ call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ callvirt instance string [System.Runtime]System.Object::ToString()
+ ret
+ }
+
+ .method public hidebysig static
+ string TypeSubstitutionFieldAllowByRefLikeIntoNonByRefLike() cil managed
+ {
+ ldtoken valuetype InvalidCSharpNegative.GenericValueTypeWrapper_Invalid`1<valuetype ByRefLikeType>
+ call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ callvirt instance string [System.Runtime]System.Object::ToString()
+ ret
+ }
+
+ .method public hidebysig static
+ void OverrideMethodNotByRefLike() cil managed
+ {
+ .locals init (
+ [0] class InvalidCSharpNegative.BaseClassWithGenericMethod
+ )
+ call class InvalidCSharpNegative.BaseClassWithGenericMethod InvalidCSharpNegative.DerivedClassWithGenericMethod_Invalid::Create()
+ stloc.0
+ ldloc.0
+ callvirt instance void InvalidCSharpNegative.BaseClassWithGenericMethod::AcceptsByRefLike<valuetype ByRefLikeType>()
+ ret
+ }
+} \ No newline at end of file
diff --git a/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharpNegative.ilproj b/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharpNegative.ilproj
new file mode 100644
index 000000000000..6b8ac61f3da1
--- /dev/null
+++ b/src/tests/Loader/classloader/generics/ByRefLike/InvalidCSharpNegative.ilproj
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+ <PropertyGroup>
+ <OutputType>library</OutputType>
+ <MonoAotIncompatible>true</MonoAotIncompatible>
+ <DisableProjectBuild Condition="'$(RuntimeFlavor)' == 'Mono'">true</DisableProjectBuild>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="InvalidCSharpNegative.il" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/tests/Loader/classloader/generics/ByRefLike/ValidateNegative.cs b/src/tests/Loader/classloader/generics/ByRefLike/ValidateNegative.cs
new file mode 100644
index 000000000000..7660130f253d
--- /dev/null
+++ b/src/tests/Loader/classloader/generics/ByRefLike/ValidateNegative.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using InvalidCSharpNegative;
+
+using Xunit;
+
+public class ValidateNegative
+{
+ [Fact]
+ [SkipOnMono("Mono does not support ByRefLike generics yet")]
+ public static void AllowByRefLike_Substituted_For_NonByRefLike_Invalid()
+ {
+ Console.WriteLine($"{nameof(AllowByRefLike_Substituted_For_NonByRefLike_Invalid)}...");
+
+ Assert.Throws<TypeLoadException>(() => { Exec.TypeSubstitutionInterfaceImplementationAllowByRefLikeIntoNonByRefLike(); });
+ Assert.Throws<TypeLoadException>(() => { Exec.TypeSubstitutionInheritanceAllowByRefLikeIntoNonByRefLike(); });
+ Assert.Throws<TypeLoadException>(() => { Exec.TypeSubstitutionFieldAllowByRefLikeIntoNonByRefLike(); });
+ Assert.Throws<TypeLoadException>(() => { Exec.OverrideMethodNotByRefLike(); });
+ }
+} \ No newline at end of file
diff --git a/src/tests/Loader/classloader/generics/ByRefLike/ValidateNegative.csproj b/src/tests/Loader/classloader/generics/ByRefLike/ValidateNegative.csproj
new file mode 100644
index 000000000000..433b007bbca0
--- /dev/null
+++ b/src/tests/Loader/classloader/generics/ByRefLike/ValidateNegative.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <!-- Needed for mechanical merging of all remaining tests, this particular project may not actually need process isolation -->
+ <RequiresProcessIsolation>true</RequiresProcessIsolation>
+ <DisableProjectBuild Condition="'$(RuntimeFlavor)' == 'Mono'">true</DisableProjectBuild>
+
+ <!-- Crossgen is not intended to serve as an IL verifier, and is not designed to provide error semantics on bad input. -->
+ <CrossGenTest>false</CrossGenTest>
+ <NativeAotIncompatible>true</NativeAotIncompatible>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="ValidateNegative.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="$(TestLibraryProjectPath)" />
+ <ProjectReference Include="InvalidCSharpNegative.ilproj" />
+ </ItemGroup>
+</Project>