diff options
author | Aaron Robinson <arobins@microsoft.com> | 2024-03-01 19:53:48 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-01 19:53:48 -0800 |
commit | 2ab51c6ef3fb24a625c1f4729f9b2e626a0f141b (patch) | |
tree | d7bd2633bdbc0e8c5647083d9c7e91ec86f310b3 | |
parent | 6fa046837910fcc7a7dbd64be4b1ad98f9ea5db3 (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.
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` – The box, `isint`, and unbox target types are all equal. `box` ; `isinst` ; `br_true/false` – 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> |