diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs index 6f85759dee0f..c5dae364a898 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameterConstraints.cs @@ -40,6 +40,12 @@ public override void Populate(TextWriter trapFile) if (Symbol.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated) trapFile.general_type_parameter_constraints(this, 5); + if (Symbol.HasNotNullConstraint) + trapFile.general_type_parameter_constraints(this, 6); + + if (Symbol.AllowsRefLikeType) + trapFile.general_type_parameter_constraints(this, 7); + foreach (var abase in Symbol.GetAnnotatedTypeConstraints()) { var t = Type.Create(Context, abase.Symbol); diff --git a/csharp/ql/lib/change-notes/2025-01-03-allow-ref-struct.md b/csharp/ql/lib/change-notes/2025-01-03-allow-ref-struct.md new file mode 100644 index 000000000000..c9a7234fa202 --- /dev/null +++ b/csharp/ql/lib/change-notes/2025-01-03-allow-ref-struct.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* C# 13: Added extractor support and call dispatch logic (data flow) for the (negative) type parameter constraint `allows ref struct`. Added extractor support for the type parameter constraint `notnull`. diff --git a/csharp/ql/lib/semmle/code/csharp/Conversion.qll b/csharp/ql/lib/semmle/code/csharp/Conversion.qll index 636a48c09e26..7a1314abe163 100644 --- a/csharp/ql/lib/semmle/code/csharp/Conversion.qll +++ b/csharp/ql/lib/semmle/code/csharp/Conversion.qll @@ -649,11 +649,14 @@ predicate convBoxing(Type fromType, Type toType) { } private predicate convBoxingValueType(ValueType fromType, Type toType) { - toType instanceof ObjectType - or - toType instanceof DynamicType - or - toType instanceof SystemValueTypeClass + ( + toType instanceof ObjectType + or + toType instanceof DynamicType + or + toType instanceof SystemValueTypeClass + ) and + not fromType.isRefLikeType() or toType = fromType.getABaseInterface+() } diff --git a/csharp/ql/lib/semmle/code/csharp/Generics.qll b/csharp/ql/lib/semmle/code/csharp/Generics.qll index 4790c9637fc4..81535fc1008a 100644 --- a/csharp/ql/lib/semmle/code/csharp/Generics.qll +++ b/csharp/ql/lib/semmle/code/csharp/Generics.qll @@ -287,6 +287,12 @@ class TypeParameterConstraints extends Element, @type_parameter_constraints { /** Holds if these constraints include a nullable reference type constraint. */ predicate hasNullableRefTypeConstraint() { general_type_parameter_constraints(this, 5) } + /** Holds if these constraints include a notnull type constraint. */ + predicate hasNotNullTypeConstraint() { general_type_parameter_constraints(this, 6) } + + /** Holds if these constraints include a `allows ref struct` constraint. */ + predicate hasAllowRefLikeTypeConstraint() { general_type_parameter_constraints(this, 7) } + /** Gets a textual representation of these constraints. */ override string toString() { result = "where " + this.getTypeParameter().getName() + ": ..." } diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index e5a2c1b07c1e..784ae5c69782 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -48,6 +48,11 @@ class Type extends Member, TypeContainer, @type { /** Holds if this type is a value type, or a type parameter that is a value type. */ predicate isValueType() { none() } + + /** + * Holds if this type is a ref like type. + */ + predicate isRefLikeType() { none() } } pragma[nomagic] @@ -704,8 +709,12 @@ class Enum extends ValueType, @enum_type { * ``` */ class Struct extends ValueType, @struct_type { - /** Holds if this `struct` has a `ref` modifier. */ - predicate isRef() { this.hasModifier("ref") } + /** + * DEPRECATED: Use `instanceof RefStruct` instead. + * + * Holds if this `struct` has a `ref` modifier. + */ + deprecated predicate isRef() { this.hasModifier("ref") } /** Holds if this `struct` has a `readonly` modifier. */ predicate isReadonly() { this.hasModifier("readonly") } @@ -713,6 +722,25 @@ class Struct extends ValueType, @struct_type { override string getAPrimaryQlClass() { result = "Struct" } } +/** + * A `ref struct`, for example + * + * ```csharp + * ref struct S { + * ... + * } + * ``` + */ +class RefStruct extends Struct { + RefStruct() { this.hasModifier("ref") } + + override string getAPrimaryQlClass() { result = "RefStruct" } + + override predicate isValueType() { none() } + + override predicate isRefLikeType() { any() } +} + /** * A `record struct`, for example * ```csharp diff --git a/csharp/ql/lib/semmle/code/csharp/Unification.qll b/csharp/ql/lib/semmle/code/csharp/Unification.qll index 30a94efbe6fb..c8b78cd07a27 100644 --- a/csharp/ql/lib/semmle/code/csharp/Unification.qll +++ b/csharp/ql/lib/semmle/code/csharp/Unification.qll @@ -522,16 +522,21 @@ module Gvn { /** Provides definitions related to type unification. */ module Unification { - /** A type parameter that is compatible with any type. */ + /** A type parameter that is compatible with any type except `ref struct`. */ class UnconstrainedTypeParameter extends TypeParameter { - UnconstrainedTypeParameter() { not exists(getATypeConstraint(this)) } + UnconstrainedTypeParameter() { + not exists(getATypeConstraint(this)) and not exists(getANegativeTypeConstraint(this)) + } } /** A type parameter that is constrained. */ class ConstrainedTypeParameter extends TypeParameter { int constraintCount; - ConstrainedTypeParameter() { constraintCount = strictcount(getATypeConstraint(this)) } + ConstrainedTypeParameter() { + constraintCount = count(getATypeConstraint(this)) + count(getANegativeTypeConstraint(this)) and + constraintCount > 0 + } /** * Holds if this type parameter is unifiable with type `t`. @@ -559,7 +564,7 @@ module Unification { bindingset[this] pragma[inline_late] override predicate unifiable(Type t) { - exists(TTypeParameterConstraint ttc | ttc = getATypeConstraint(this) | + forall(TTypeParameterConstraint ttc | ttc = getATypeConstraint(this) | ttc = TRefTypeConstraint() and t.isRefType() or @@ -567,13 +572,14 @@ module Unification { t.isValueType() or typeConstraintUnifiable(ttc, t) - ) + ) and + (t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint()) } bindingset[this] pragma[inline_late] override predicate subsumes(Type t) { - exists(TTypeParameterConstraint ttc | ttc = getATypeConstraint(this) | + forall(TTypeParameterConstraint ttc | ttc = getATypeConstraint(this) | ttc = TRefTypeConstraint() and t.isRefType() or @@ -581,7 +587,8 @@ module Unification { t.isValueType() or typeConstraintSubsumes(ttc, t) - ) + ) and + (t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint()) } } @@ -603,7 +610,8 @@ module Unification { t.isValueType() or typeConstraintUnifiable(ttc, t) - ) + ) and + (t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint()) } bindingset[this] @@ -617,7 +625,8 @@ module Unification { t.isValueType() or typeConstraintSubsumes(ttc, t) - ) + ) and + (t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint()) } } @@ -632,6 +641,9 @@ module Unification { not t instanceof TypeParameter } + cached + newtype TTypeParameterNegativeConstraint = TAllowRefTypeConstraint() + cached TTypeParameterConstraint getATypeConstraint(TypeParameter tp) { exists(TypeParameterConstraints tpc | tpc = tp.getConstraints() | @@ -650,6 +662,14 @@ module Unification { ) } + cached + TTypeParameterNegativeConstraint getANegativeTypeConstraint(TypeParameter tp) { + exists(TypeParameterConstraints tpc | tpc = tp.getConstraints() | + tpc.hasAllowRefLikeTypeConstraint() and + result = TAllowRefTypeConstraint() + ) + } + cached predicate typeConstraintUnifiable(TTypeConstraint ttc, Type t) { exists(Type t0 | ttc = TTypeConstraint(t0) | implicitConversionRestricted(t, t0)) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index a66c7f3c5d83..e3e9ef4e86be 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -703,7 +703,7 @@ module LocalFlow { or t = any(TypeParameter tp | not tp.isValueType()) or - t.(Struct).isRef() + t.isRefLikeType() ) and not exists(getALastEvalNode(result)) } diff --git a/csharp/ql/test/library-tests/conversion/boxing/Boxing.cs b/csharp/ql/test/library-tests/conversion/boxing/Boxing.cs index a37d39aa2a04..dbfe4acac4b9 100644 --- a/csharp/ql/test/library-tests/conversion/boxing/Boxing.cs +++ b/csharp/ql/test/library-tests/conversion/boxing/Boxing.cs @@ -45,3 +45,6 @@ void M() x1 = x15; // not a boxing conversion } } + +// Ref structs can't be converted to a dynamic, object or valuetype. +ref struct S { } diff --git a/csharp/ql/test/library-tests/csharp11/PrintAst.expected b/csharp/ql/test/library-tests/csharp11/PrintAst.expected index dc1927360e1b..b10e2096489b 100644 --- a/csharp/ql/test/library-tests/csharp11/PrintAst.expected +++ b/csharp/ql/test/library-tests/csharp11/PrintAst.expected @@ -847,7 +847,7 @@ RequiredMembers.cs: # 40| 0: [Parameter] value Scoped.cs: # 1| [Struct] S1 -# 2| [Struct] S2 +# 2| [RefStruct] S2 # 7| [Class] ScopedModifierTest # 9| 5: [Method] M1 # 9| -1: [TypeMention] int @@ -1402,7 +1402,7 @@ Strings.cs: Struct.cs: # 1| [NamespaceDeclaration] namespace ... { ... } # 3| 1: [Class] MyEmptyClass -# 5| 2: [Struct] RefStruct +# 5| 2: [RefStruct] RefStruct # 7| 5: [Field] MyInt # 7| -1: [TypeMention] int # 8| 6: [Field] MyByte diff --git a/csharp/ql/test/library-tests/csharp7.2/PrintAst.expected b/csharp/ql/test/library-tests/csharp7.2/PrintAst.expected index 31036ced693c..834b03b44715 100644 --- a/csharp/ql/test/library-tests/csharp7.2/PrintAst.expected +++ b/csharp/ql/test/library-tests/csharp7.2/PrintAst.expected @@ -30,8 +30,8 @@ csharp72.cs: # 26| 0: [FieldAccess] access to field s # 29| 7: [DelegateType] Del # 32| [Struct] ReadonlyStruct -# 36| [Struct] RefStruct -# 40| [Struct] ReadonlyRefStruct +# 36| [RefStruct] RefStruct +# 40| [RefStruct] ReadonlyRefStruct # 44| [Class] NumericLiterals # 46| 5: [Field] binaryValue # 46| -1: [TypeMention] int diff --git a/csharp/ql/test/library-tests/csharp7.2/RefStructs.ql b/csharp/ql/test/library-tests/csharp7.2/RefStructs.ql index 556a0b156d8f..4ad75b22bdfe 100644 --- a/csharp/ql/test/library-tests/csharp7.2/RefStructs.ql +++ b/csharp/ql/test/library-tests/csharp7.2/RefStructs.ql @@ -1,7 +1,5 @@ import csharp -from Struct s -where - s.fromSource() and - s.isRef() +from RefStruct s +where s.fromSource() select s diff --git a/csharp/ql/test/library-tests/dispatch/CallContext.expected b/csharp/ql/test/library-tests/dispatch/CallContext.expected index 7234249cbff1..b5f75b4f265c 100644 --- a/csharp/ql/test/library-tests/dispatch/CallContext.expected +++ b/csharp/ql/test/library-tests/dispatch/CallContext.expected @@ -26,3 +26,4 @@ mayBenefitFromCallContext | ViableCallable.cs:576:18:576:22 | call to operator / | | ViableCallable.cs:579:26:579:30 | call to operator checked / | | ViableCallable.cs:585:9:585:15 | call to method M12 | +| ViableCallable.cs:618:9:618:13 | call to method M | diff --git a/csharp/ql/test/library-tests/dispatch/CallGraph.expected b/csharp/ql/test/library-tests/dispatch/CallGraph.expected index e8087b773612..f1fc555f5203 100644 --- a/csharp/ql/test/library-tests/dispatch/CallGraph.expected +++ b/csharp/ql/test/library-tests/dispatch/CallGraph.expected @@ -259,3 +259,6 @@ | ViableCallable.cs:555:10:555:15 | Run`1 | ViableCallable.cs:550:40:550:40 | checked / | | ViableCallable.cs:555:10:555:15 | Run`1 | ViableCallable.cs:552:17:552:19 | M11 | | ViableCallable.cs:555:10:555:15 | Run`1 | ViableCallable.cs:553:17:553:19 | M12 | +| ViableCallable.cs:609:17:609:23 | Run1`1 | ViableCallable.cs:601:21:601:21 | M | +| ViableCallable.cs:615:17:615:23 | Run2`1 | ViableCallable.cs:601:21:601:21 | M | +| ViableCallable.cs:615:17:615:23 | Run2`1 | ViableCallable.cs:606:21:606:21 | M | diff --git a/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected b/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected index 8849a785a1cb..c31cd923d9d6 100644 --- a/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected +++ b/csharp/ql/test/library-tests/dispatch/GetADynamicTarget.expected @@ -505,3 +505,6 @@ | ViableCallable.cs:585:9:585:15 | call to method M12 | C20.M12() | | ViableCallable.cs:585:9:585:15 | call to method M12 | I3.M12() | | ViableCallable.cs:588:9:588:15 | call to method M13 | I3.M13() | +| ViableCallable.cs:612:9:612:13 | call to method M | C21+A1.M() | +| ViableCallable.cs:618:9:618:13 | call to method M | C21+A1.M() | +| ViableCallable.cs:618:9:618:13 | call to method M | C21+A2.M() | diff --git a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs index 55a67b69e0d6..49210d992afc 100644 --- a/csharp/ql/test/library-tests/dispatch/ViableCallable.cs +++ b/csharp/ql/test/library-tests/dispatch/ViableCallable.cs @@ -588,3 +588,33 @@ void Run(T c) where T : I3 c.M13(); } } + +public class C21 +{ + public interface I + { + void M(); + } + + public class A1 : I + { + public void M() { } + } + + public ref struct A2 : I + { + public void M() { } + } + + public void Run1(T t) where T : I + { + // Viable callable: A1.M() + t.M(); + } + + public void Run2(T t) where T : I, allows ref struct + { + // Viable callable: {A1, A2}.M() + t.M(); + } +} diff --git a/csharp/ql/test/library-tests/typeparameterconstraints/TypeParameterConstraints.cs b/csharp/ql/test/library-tests/typeparameterconstraints/TypeParameterConstraints.cs new file mode 100644 index 000000000000..a284d53713cb --- /dev/null +++ b/csharp/ql/test/library-tests/typeparameterconstraints/TypeParameterConstraints.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +public class TestClass +{ + public void M1(T1 x) where T1 : class { } + + public void M2(T2 x) where T2 : struct { } + + public void M3(T3 x) where T3 : unmanaged { } + + public void M4(T4 x) where T4 : new() { } + + public void M5(T5 x) where T5 : notnull { } + + public void M6(T6 x) where T6 : IList { } + + public void M7(T7 x) where T7 : allows ref struct { } +} diff --git a/csharp/ql/test/library-tests/typeparameterconstraints/typeParameterConstraints.expected b/csharp/ql/test/library-tests/typeparameterconstraints/typeParameterConstraints.expected new file mode 100644 index 000000000000..3565e727cd64 --- /dev/null +++ b/csharp/ql/test/library-tests/typeparameterconstraints/typeParameterConstraints.expected @@ -0,0 +1,24 @@ +typeParameterContraints +| TypeParameterConstraints.cs:6:20:6:21 | T1 | file://:0:0:0:0 | where T1: ... | +| TypeParameterConstraints.cs:8:20:8:21 | T2 | file://:0:0:0:0 | where T2: ... | +| TypeParameterConstraints.cs:10:20:10:21 | T3 | file://:0:0:0:0 | where T3: ... | +| TypeParameterConstraints.cs:12:20:12:21 | T4 | file://:0:0:0:0 | where T4: ... | +| TypeParameterConstraints.cs:14:20:14:21 | T5 | file://:0:0:0:0 | where T5: ... | +| TypeParameterConstraints.cs:16:20:16:21 | T6 | file://:0:0:0:0 | where T6: ... | +| TypeParameterConstraints.cs:18:20:18:21 | T7 | file://:0:0:0:0 | where T7: ... | +specificParameterConstraints +| TypeParameterConstraints.cs:16:20:16:21 | T6 | IList | +hasConstructorConstraint +| TypeParameterConstraints.cs:12:20:12:21 | T4 | file://:0:0:0:0 | where T4: ... | +hasRefTypeConstraint +| TypeParameterConstraints.cs:6:20:6:21 | T1 | file://:0:0:0:0 | where T1: ... | +hasValueTypeConstraint +| TypeParameterConstraints.cs:8:20:8:21 | T2 | file://:0:0:0:0 | where T2: ... | +| TypeParameterConstraints.cs:10:20:10:21 | T3 | file://:0:0:0:0 | where T3: ... | +hasUnmanagedTypeConstraint +| TypeParameterConstraints.cs:10:20:10:21 | T3 | file://:0:0:0:0 | where T3: ... | +hasNullableRefTypeConstraint +hasNotNullConstraint +| TypeParameterConstraints.cs:14:20:14:21 | T5 | file://:0:0:0:0 | where T5: ... | +hasAllowRefLikeTypeConstraint +| TypeParameterConstraints.cs:18:20:18:21 | T7 | file://:0:0:0:0 | where T7: ... | diff --git a/csharp/ql/test/library-tests/typeparameterconstraints/typeParameterConstraints.ql b/csharp/ql/test/library-tests/typeparameterconstraints/typeParameterConstraints.ql new file mode 100644 index 000000000000..3c82c8bab14f --- /dev/null +++ b/csharp/ql/test/library-tests/typeparameterconstraints/typeParameterConstraints.ql @@ -0,0 +1,39 @@ +import csharp + +query predicate typeParameterContraints(TypeParameter tp, TypeParameterConstraints tpc) { + tp.fromSource() and tp.getConstraints() = tpc +} + +query predicate specificParameterConstraints(TypeParameter tp, string type) { + exists(TypeParameterConstraints tpc | + typeParameterContraints(tp, tpc) and type = tpc.getATypeConstraint().toStringWithTypes() + ) +} + +query predicate hasConstructorConstraint(TypeParameter tp, TypeParameterConstraints tpc) { + typeParameterContraints(tp, tpc) and tpc.hasConstructorConstraint() +} + +query predicate hasRefTypeConstraint(TypeParameter tp, TypeParameterConstraints tpc) { + typeParameterContraints(tp, tpc) and tpc.hasRefTypeConstraint() +} + +query predicate hasValueTypeConstraint(TypeParameter tp, TypeParameterConstraints tpc) { + typeParameterContraints(tp, tpc) and tpc.hasValueTypeConstraint() +} + +query predicate hasUnmanagedTypeConstraint(TypeParameter tp, TypeParameterConstraints tpc) { + typeParameterContraints(tp, tpc) and tpc.hasUnmanagedTypeConstraint() +} + +query predicate hasNullableRefTypeConstraint(TypeParameter tp, TypeParameterConstraints tpc) { + typeParameterContraints(tp, tpc) and tpc.hasNullableRefTypeConstraint() +} + +query predicate hasNotNullConstraint(TypeParameter tp, TypeParameterConstraints tpc) { + typeParameterContraints(tp, tpc) and tpc.hasNotNullTypeConstraint() +} + +query predicate hasAllowRefLikeTypeConstraint(TypeParameter tp, TypeParameterConstraints tpc) { + typeParameterContraints(tp, tpc) and tpc.hasAllowRefLikeTypeConstraint() +} diff --git a/csharp/ql/test/library-tests/unification/Unification.cs b/csharp/ql/test/library-tests/unification/Unification.cs index e392911030c8..65d06e9c1397 100644 --- a/csharp/ql/test/library-tests/unification/Unification.cs +++ b/csharp/ql/test/library-tests/unification/Unification.cs @@ -48,3 +48,11 @@ public class NestedC { } Nested.NestedB.NestedC x5; Nested.NestedB.NestedC x6; } + +interface I2 { } +struct S3 : I2 { } +ref struct RS : I2 { } +class C7 : I2 { } + +class NormalConstraint where T : I2 { } +class NegativeConstraint where T : I2, allows ref struct { } diff --git a/csharp/ql/test/library-tests/unification/Unification.expected b/csharp/ql/test/library-tests/unification/Unification.expected index 7f837911baca..9849938a4a68 100644 --- a/csharp/ql/test/library-tests/unification/Unification.expected +++ b/csharp/ql/test/library-tests/unification/Unification.expected @@ -7,6 +7,7 @@ constrainedTypeParameterSubsumes | Unification.cs:8:10:8:11 | T2 | Unification.cs:30:12:30:24 | (string, int) | | Unification.cs:8:10:8:11 | T2 | Unification.cs:31:12:31:23 | (string, T9) | | Unification.cs:8:10:8:11 | T2 | Unification.cs:32:12:32:19 | (T8, T9) | +| Unification.cs:8:10:8:11 | T2 | Unification.cs:53:8:53:9 | S3 | | Unification.cs:9:10:9:11 | T3 | Unification.cs:1:11:1:12 | I1 | | Unification.cs:9:10:9:11 | T3 | Unification.cs:6:7:6:8 | C0 | | Unification.cs:9:10:9:11 | T3 | Unification.cs:7:7:7:12 | C1 | @@ -57,6 +58,10 @@ constrainedTypeParameterSubsumes | Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested+NestedB+NestedC | | Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested+NestedB+NestedC | | Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC | +| Unification.cs:9:10:9:11 | T3 | Unification.cs:52:11:52:12 | I2 | +| Unification.cs:9:10:9:11 | T3 | Unification.cs:55:7:55:8 | C7 | +| Unification.cs:9:10:9:11 | T3 | Unification.cs:57:7:57:25 | NormalConstraint | +| Unification.cs:9:10:9:11 | T3 | Unification.cs:58:7:58:27 | NegativeConstraint | | Unification.cs:10:10:10:11 | T4 | Unification.cs:7:7:7:12 | C1 | | Unification.cs:10:10:10:11 | T4 | Unification.cs:10:10:10:11 | T4 | | Unification.cs:11:10:11:11 | T5 | Unification.cs:8:7:8:12 | C2 | @@ -96,8 +101,20 @@ constrainedTypeParameterSubsumes | Unification.cs:12:25:12:27 | T6d | Unification.cs:30:12:30:24 | (string, int) | | Unification.cs:12:25:12:27 | T6d | Unification.cs:31:12:31:23 | (string, T9) | | Unification.cs:12:25:12:27 | T6d | Unification.cs:32:12:32:19 | (T8, T9) | +| Unification.cs:12:25:12:27 | T6d | Unification.cs:53:8:53:9 | S3 | | Unification.cs:24:12:24:13 | Tm | Unification.cs:8:7:8:12 | C2 | | Unification.cs:24:12:24:13 | Tm | Unification.cs:24:12:24:13 | Tm | +| Unification.cs:57:24:57:24 | T | Unification.cs:52:11:52:12 | I2 | +| Unification.cs:57:24:57:24 | T | Unification.cs:53:8:53:9 | S3 | +| Unification.cs:57:24:57:24 | T | Unification.cs:55:7:55:8 | C7 | +| Unification.cs:57:24:57:24 | T | Unification.cs:57:24:57:24 | T | +| Unification.cs:57:24:57:24 | T | Unification.cs:58:26:58:26 | T | +| Unification.cs:58:26:58:26 | T | Unification.cs:52:11:52:12 | I2 | +| Unification.cs:58:26:58:26 | T | Unification.cs:53:8:53:9 | S3 | +| Unification.cs:58:26:58:26 | T | Unification.cs:54:12:54:13 | RS | +| Unification.cs:58:26:58:26 | T | Unification.cs:55:7:55:8 | C7 | +| Unification.cs:58:26:58:26 | T | Unification.cs:57:24:57:24 | T | +| Unification.cs:58:26:58:26 | T | Unification.cs:58:26:58:26 | T | constrainedTypeParameterSubsumptionImpliesUnification constrainedTypeParameterUnifiable | Unification.cs:8:10:8:11 | T2 | Unification.cs:3:8:3:9 | S1 | @@ -108,6 +125,7 @@ constrainedTypeParameterUnifiable | Unification.cs:8:10:8:11 | T2 | Unification.cs:30:12:30:24 | (string, int) | | Unification.cs:8:10:8:11 | T2 | Unification.cs:31:12:31:23 | (string, T9) | | Unification.cs:8:10:8:11 | T2 | Unification.cs:32:12:32:19 | (T8, T9) | +| Unification.cs:8:10:8:11 | T2 | Unification.cs:53:8:53:9 | S3 | | Unification.cs:9:10:9:11 | T3 | Unification.cs:1:11:1:12 | I1 | | Unification.cs:9:10:9:11 | T3 | Unification.cs:6:7:6:8 | C0 | | Unification.cs:9:10:9:11 | T3 | Unification.cs:7:7:7:12 | C1 | @@ -158,6 +176,10 @@ constrainedTypeParameterUnifiable | Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested+NestedB+NestedC | | Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested+NestedB+NestedC | | Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC | +| Unification.cs:9:10:9:11 | T3 | Unification.cs:52:11:52:12 | I2 | +| Unification.cs:9:10:9:11 | T3 | Unification.cs:55:7:55:8 | C7 | +| Unification.cs:9:10:9:11 | T3 | Unification.cs:57:7:57:25 | NormalConstraint | +| Unification.cs:9:10:9:11 | T3 | Unification.cs:58:7:58:27 | NegativeConstraint | | Unification.cs:10:10:10:11 | T4 | Unification.cs:7:7:7:12 | C1 | | Unification.cs:10:10:10:11 | T4 | Unification.cs:7:7:7:12 | C1 | | Unification.cs:10:10:10:11 | T4 | Unification.cs:7:7:7:12 | C1 | @@ -205,9 +227,21 @@ constrainedTypeParameterUnifiable | Unification.cs:12:25:12:27 | T6d | Unification.cs:30:12:30:24 | (string, int) | | Unification.cs:12:25:12:27 | T6d | Unification.cs:31:12:31:23 | (string, T9) | | Unification.cs:12:25:12:27 | T6d | Unification.cs:32:12:32:19 | (T8, T9) | +| Unification.cs:12:25:12:27 | T6d | Unification.cs:53:8:53:9 | S3 | | Unification.cs:24:12:24:13 | Tm | Unification.cs:8:7:8:12 | C2 | | Unification.cs:24:12:24:13 | Tm | Unification.cs:8:7:8:12 | C2 | | Unification.cs:24:12:24:13 | Tm | Unification.cs:24:12:24:13 | Tm | +| Unification.cs:57:24:57:24 | T | Unification.cs:52:11:52:12 | I2 | +| Unification.cs:57:24:57:24 | T | Unification.cs:53:8:53:9 | S3 | +| Unification.cs:57:24:57:24 | T | Unification.cs:55:7:55:8 | C7 | +| Unification.cs:57:24:57:24 | T | Unification.cs:57:24:57:24 | T | +| Unification.cs:57:24:57:24 | T | Unification.cs:58:26:58:26 | T | +| Unification.cs:58:26:58:26 | T | Unification.cs:52:11:52:12 | I2 | +| Unification.cs:58:26:58:26 | T | Unification.cs:53:8:53:9 | S3 | +| Unification.cs:58:26:58:26 | T | Unification.cs:54:12:54:13 | RS | +| Unification.cs:58:26:58:26 | T | Unification.cs:55:7:55:8 | C7 | +| Unification.cs:58:26:58:26 | T | Unification.cs:57:24:57:24 | T | +| Unification.cs:58:26:58:26 | T | Unification.cs:58:26:58:26 | T | subsumes | Unification.cs:7:7:7:12 | C1 | Unification.cs:7:7:7:12 | C1 | | Unification.cs:7:7:7:12 | C1 | Unification.cs:7:7:7:12 | C1 | @@ -312,6 +346,8 @@ subsumes | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC | Unification.cs:41:22:41:33 | Nested+NestedB+NestedC | | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC | Unification.cs:41:22:41:33 | Nested+NestedB+NestedC | | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC | +| Unification.cs:57:7:57:25 | NormalConstraint | Unification.cs:57:7:57:25 | NormalConstraint | +| Unification.cs:58:7:58:27 | NegativeConstraint | Unification.cs:58:7:58:27 | NegativeConstraint | subsumptionImpliesUnification unifiable | Unification.cs:7:7:7:12 | C1 | Unification.cs:7:7:7:12 | C1 |