diff options
6 files changed, 395 insertions, 0 deletions
diff --git a/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs b/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs new file mode 100644 index 000000000..31d1d293c --- /dev/null +++ b/src/Common/src/TypeSystem/Common/TypeSystemConstaintsHelpers.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System.Diagnostics; + +namespace Internal.TypeSystem +{ + public static class TypeSystemConstraintsHelpers + { + private static bool VerifyGenericParamConstraint(Instantiation typeInstantiation, Instantiation methodInstantiation, GenericParameterDesc genericParam, TypeDesc instantiationParam) + { + // Check class constraint + if (genericParam.HasReferenceTypeConstraint && !instantiationParam.IsGCPointer) + return false; + + // Check default constructor constraint + if (genericParam.HasDefaultConstructorConstraint) + { + if (!instantiationParam.IsDefType) + return false; + + if (!instantiationParam.IsValueType && instantiationParam.GetDefaultConstructor() == null) + return false; + } + + // Check struct constraint + if (genericParam.HasNotNullableValueTypeConstraint) + { + if (!instantiationParam.IsValueType) + return false; + + if (instantiationParam.IsNullable) + return false; + } + + foreach (var constraintType in genericParam.TypeConstraints) + { + var instantiatedType = constraintType.InstantiateSignature(typeInstantiation, methodInstantiation); + if (!instantiationParam.CanCastTo(instantiatedType)) + return false; + } + + return true; + } + + public static bool CheckConstraints(this TypeDesc type) + { + // Non-generic types always pass constraints check + if (!type.HasInstantiation) + return true; + + TypeDesc uninstantiatedType = type.GetTypeDefinition(); + for (int i = 0; i < uninstantiatedType.Instantiation.Length; i++) + { + if (!VerifyGenericParamConstraint(type.Instantiation, default(Instantiation), (GenericParameterDesc)uninstantiatedType.Instantiation[i], type.Instantiation[i])) + return false; + } + + return true; + } + + public static bool CheckConstraints(this MethodDesc method) + { + if (!method.OwningType.CheckConstraints()) + return false; + + // Non-generic methods always pass constraints check + if (!method.HasInstantiation) + return true; + + MethodDesc uninstantiatedMethod = method.GetMethodDefinition(); + for (int i = 0; i < uninstantiatedMethod.Instantiation.Length; i++) + { + if (!VerifyGenericParamConstraint(method.OwningType.Instantiation, method.Instantiation, (GenericParameterDesc)uninstantiatedMethod.Instantiation[i], method.Instantiation[i])) + return false; + } + + return true; + } + } +} diff --git a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj index aac62c84e..e2ea5f3cd 100644 --- a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj +++ b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj @@ -212,6 +212,9 @@ <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemHelpers.cs"> <Link>TypeSystem\Common\TypeSystemHelpers.cs</Link> </Compile> + <Compile Include="..\..\Common\src\TypeSystem\Common\TypeSystemConstaintsHelpers.cs"> + <Link>TypeSystem\Common\TypeSystemConstaintsHelpers.cs</Link> + </Compile> <Compile Include="..\..\Common\src\TypeSystem\Common\Utilities\TypeNameFormatter.cs"> <Link>Utilities\TypeNameFormatter.cs</Link> </Compile> diff --git a/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs b/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs new file mode 100644 index 000000000..3b4d12395 --- /dev/null +++ b/src/ILCompiler.TypeSystem/tests/ConstraintsValidationTest.cs @@ -0,0 +1,254 @@ +// Licensed to the.NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.TypeSystem; + +using Xunit; + +namespace TypeSystemTests +{ + public class ConstraintsValidationTest + { + private TestTypeSystemContext _context; + private ModuleDesc _testModule; + + private MetadataType _iNonGenType; + private MetadataType _iGenType; + private MetadataType _arg1Type; + private MetadataType _arg2Type; + private MetadataType _arg3Type; + private MetadataType _structArgWithDefaultCtorType; + private MetadataType _structArgWithoutDefaultCtorType; + private MetadataType _classArgWithDefaultCtorType; + private MetadataType _classArgWithoutDefaultCtorType; + private MetadataType _referenceTypeConstraintType; + private MetadataType _defaultConstructorConstraintType; + private MetadataType _notNullableValueTypeConstraintType; + private MetadataType _simpleTypeConstraintType; + private MetadataType _doubleSimpleTypeConstraintType; + private MetadataType _simpleGenericConstraintType; + private MetadataType _complexGenericConstraint1Type; + private MetadataType _complexGenericConstraint2Type; + private MetadataType _complexGenericConstraint3Type; + private MetadataType _multipleConstraintsType; + + public ConstraintsValidationTest() + { + _context = new TestTypeSystemContext(TargetArchitecture.Unknown); + var systemModule = _context.CreateModuleForSimpleName("CoreTestAssembly"); + _context.SetSystemModule(systemModule); + + _testModule = systemModule; + + _iNonGenType = _testModule.GetType("GenericConstraints", "INonGen"); + _iGenType = _testModule.GetType("GenericConstraints", "IGen`1"); + _arg1Type = _testModule.GetType("GenericConstraints", "Arg1"); + _arg2Type = _testModule.GetType("GenericConstraints", "Arg2`1"); + _arg3Type = _testModule.GetType("GenericConstraints", "Arg3`1"); + _structArgWithDefaultCtorType = _testModule.GetType("GenericConstraints", "StructArgWithDefaultCtor"); + _structArgWithoutDefaultCtorType = _testModule.GetType("GenericConstraints", "StructArgWithoutDefaultCtor"); + _classArgWithDefaultCtorType = _testModule.GetType("GenericConstraints", "ClassArgWithDefaultCtor"); + _classArgWithoutDefaultCtorType = _testModule.GetType("GenericConstraints", "ClassArgWithoutDefaultCtor"); + + _referenceTypeConstraintType = _testModule.GetType("GenericConstraints", "ReferenceTypeConstraint`1"); + _defaultConstructorConstraintType = _testModule.GetType("GenericConstraints", "DefaultConstructorConstraint`1"); + _notNullableValueTypeConstraintType = _testModule.GetType("GenericConstraints", "NotNullableValueTypeConstraint`1"); + _simpleTypeConstraintType = _testModule.GetType("GenericConstraints", "SimpleTypeConstraint`1"); + _doubleSimpleTypeConstraintType = _testModule.GetType("GenericConstraints", "DoubleSimpleTypeConstraint`1"); + _simpleGenericConstraintType = _testModule.GetType("GenericConstraints", "SimpleGenericConstraint`2"); + _complexGenericConstraint1Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint1`2"); + _complexGenericConstraint2Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint2`2"); + _complexGenericConstraint3Type = _testModule.GetType("GenericConstraints", "ComplexGenericConstraint3`2"); + _multipleConstraintsType = _testModule.GetType("GenericConstraints", "MultipleConstraints`2"); + } + + [Fact] + public void TestTypeConstraints() + { + MetadataType instantiatedType; + + MetadataType arg2OfInt = _arg2Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + MetadataType arg2OfBool = _arg2Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Boolean)); + MetadataType arg2OfObject = _arg2Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Object)); + + // ReferenceTypeConstraint + { + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_arg1Type); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_iNonGenType); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_structArgWithDefaultCtorType); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _referenceTypeConstraintType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + Assert.False(instantiatedType.CheckConstraints()); + } + + // DefaultConstructorConstraint + { + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_arg1Type); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_classArgWithDefaultCtorType); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_classArgWithoutDefaultCtorType); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_structArgWithDefaultCtorType); + Assert.True(instantiatedType.CheckConstraints()); + + // Structs always have implicit default constructors + instantiatedType = _defaultConstructorConstraintType.MakeInstantiatedType(_structArgWithoutDefaultCtorType); + Assert.True(instantiatedType.CheckConstraints()); + } + + // NotNullableValueTypeConstraint + { + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(_arg1Type); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(_structArgWithDefaultCtorType); + Assert.True(instantiatedType.CheckConstraints()); + + MetadataType nullable = (MetadataType)_context.GetWellKnownType(WellKnownType.Nullable); + MetadataType nullableOfInt = nullable.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Int32)); + + instantiatedType = _notNullableValueTypeConstraintType.MakeInstantiatedType(nullableOfInt); + Assert.False(instantiatedType.CheckConstraints()); + } + + // SimpleTypeConstraint and DoubleSimpleTypeConstraint + foreach(var genType in new MetadataType[] { _simpleTypeConstraintType , _doubleSimpleTypeConstraintType }) + { + instantiatedType = genType.MakeInstantiatedType(_arg1Type); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = genType.MakeInstantiatedType(_iNonGenType); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = genType.MakeInstantiatedType(_classArgWithDefaultCtorType); + Assert.False(instantiatedType.CheckConstraints()); + } + + // SimpleGenericConstraint + { + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_arg1Type, _arg1Type); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_arg1Type, _iNonGenType); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_classArgWithDefaultCtorType, _classArgWithoutDefaultCtorType); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_arg1Type, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_structArgWithDefaultCtorType, _context.GetWellKnownType(WellKnownType.ValueType)); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_arg1Type, _context.GetWellKnownType(WellKnownType.ValueType)); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.UInt16), _context.GetWellKnownType(WellKnownType.UInt32)); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _simpleGenericConstraintType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.UInt16), _context.GetWellKnownType(WellKnownType.ValueType)); + Assert.True(instantiatedType.CheckConstraints()); + } + + // ComplexGenericConstraint1 + { + instantiatedType = _complexGenericConstraint1Type.MakeInstantiatedType(_arg1Type, _arg1Type /* uninteresting */); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint1Type.MakeInstantiatedType(arg2OfInt, _arg1Type /* uninteresting */); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint1Type.MakeInstantiatedType(arg2OfBool, _arg1Type /* uninteresting */); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint1Type.MakeInstantiatedType(arg2OfObject, _arg1Type /* uninteresting */); + Assert.False(instantiatedType.CheckConstraints()); + } + + // ComplexGenericConstraint2 + { + MetadataType arg2OfArg2OfInt = _arg2Type.MakeInstantiatedType(arg2OfInt); + MetadataType arg2OfArg2OfBool = _arg2Type.MakeInstantiatedType(arg2OfBool); + MetadataType arg2OfArg2OfObject = _arg2Type.MakeInstantiatedType(arg2OfObject); + + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(_arg1Type, _context.GetWellKnownType(WellKnownType.Int32)); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfInt, _context.GetWellKnownType(WellKnownType.Int32)); + Assert.True(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfBool, _context.GetWellKnownType(WellKnownType.Int32)); + Assert.False(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfObject, _context.GetWellKnownType(WellKnownType.Int32)); + Assert.False(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfInt, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfBool, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfObject, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfInt, _context.GetWellKnownType(WellKnownType.Boolean)); + Assert.False(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfBool, _context.GetWellKnownType(WellKnownType.Boolean)); + Assert.True(instantiatedType.CheckConstraints()); + instantiatedType = _complexGenericConstraint2Type.MakeInstantiatedType(arg2OfArg2OfObject, _context.GetWellKnownType(WellKnownType.Boolean)); + Assert.False(instantiatedType.CheckConstraints()); + } + + // ComplexGenericConstraint3 + { + MetadataType igenOfObject = _iGenType.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Object)); + + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(igenOfObject, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + + // Variance-compatible instantiation argument + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(igenOfObject, _context.GetWellKnownType(WellKnownType.String)); + Assert.True(instantiatedType.CheckConstraints()); + + // Type that implements the interface + var arg3OfObject = _arg3Type.MakeInstantiatedType(_context.GetWellKnownType(WellKnownType.Object)); + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(arg3OfObject, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + + // Type that implements a variant compatible interface + instantiatedType = _complexGenericConstraint3Type.MakeInstantiatedType(arg3OfObject, _context.GetWellKnownType(WellKnownType.String)); + Assert.True(instantiatedType.CheckConstraints()); + } + + // MultipleConstraints + { + // Violate the class constraint + instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_structArgWithDefaultCtorType, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + + // Violate the new() constraint + instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_classArgWithoutDefaultCtorType, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + + // Violate the IGen<U> constraint + instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_arg1Type, _context.GetWellKnownType(WellKnownType.Object)); + Assert.False(instantiatedType.CheckConstraints()); + + // Satisfy all constraints + instantiatedType = _multipleConstraintsType.MakeInstantiatedType(_classArgWithDefaultCtorType, _context.GetWellKnownType(WellKnownType.Object)); + Assert.True(instantiatedType.CheckConstraints()); + } + } + } +} diff --git a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj index 056b0534a..74380612a 100644 --- a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj +++ b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/CoreTestAssembly.csproj @@ -29,6 +29,7 @@ <Compile Include="Canonicalization.cs" /> <Compile Include="Casting.cs" /> <Compile Include="GCPointerMap.cs" /> + <Compile Include="GenericConstraints.cs" /> <Compile Include="Hashcode.cs" /> <Compile Include="InterfaceArrangements.cs" /> <Compile Include="GenericTypes.cs" /> diff --git a/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs new file mode 100644 index 000000000..8a9f7d394 --- /dev/null +++ b/src/ILCompiler.TypeSystem/tests/CoreTestAssembly/GenericConstraints.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace GenericConstraints +{ + public interface INonGen { } + + public interface IGen<in T> { } + + public class Arg1 : INonGen { } + + public class Arg2<T> { } + + public class Arg3<T> : IGen<T> { } + + public struct StructArgWithDefaultCtor { } + + public struct StructArgWithoutDefaultCtor + { + public StructArgWithoutDefaultCtor(int argument) { } + } + + public class ClassArgWithDefaultCtor : IGen<object> + { + public ClassArgWithDefaultCtor() { } + } + + public class ClassArgWithoutDefaultCtor : IGen<object> + { + public ClassArgWithoutDefaultCtor(int argument) { } + } + + public class ReferenceTypeConstraint<T> where T : class { } + + public class DefaultConstructorConstraint<T> where T : new() { } + + public class NotNullableValueTypeConstraint<T> where T : struct { } + + public class SimpleTypeConstraint<T> where T : Arg1 { } + + public class DoubleSimpleTypeConstraint<T> where T : Arg1, INonGen { } + + public class SimpleGenericConstraint<T, U> where T : U { } + + public class ComplexGenericConstraint1<T, U> where T : Arg2<int> { } + + public class ComplexGenericConstraint2<T, U> where T : Arg2<Arg2<U>> { } + + public class ComplexGenericConstraint3<T, U> where T : IGen<U> { } + + public class MultipleConstraints<T, U> where T : class, IGen<U>, new() { } +} diff --git a/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj b/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj index dc84f7306..ec379925b 100644 --- a/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj +++ b/src/ILCompiler.TypeSystem/tests/TypeSystem.Tests.csproj @@ -38,6 +38,7 @@ <ItemGroup> <Compile Include="ArchitectureSpecificFieldLayoutTests.cs" /> <Compile Include="CanonicalizationTests.cs" /> + <Compile Include="ConstraintsValidationTest.cs" /> <Compile Include="GCPointerMapTests.cs" /> <Compile Include="GenericTypeAndMethodTests.cs" /> <Compile Include="CastingTests.cs" /> |