Merge pull request #18385 from michaelnebel/csharp/allowsrefstruct

C# 13: Allows ref struct.
This commit is contained in:
Michael Nebel
2025-01-13 16:04:03 +01:00
committed by GitHub
20 changed files with 256 additions and 25 deletions

View File

@@ -40,6 +40,12 @@ namespace Semmle.Extraction.CSharp.Entities
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);

View File

@@ -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`.

View File

@@ -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+()
}

View File

@@ -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() + ": ..." }

View File

@@ -48,6 +48,13 @@ 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.
*
* Only `ref struct` types are considered ref like types.
*/
predicate isRefLikeType() { none() }
}
pragma[nomagic]
@@ -704,8 +711,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 +724,23 @@ 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 isRefLikeType() { any() }
}
/**
* A `record struct`, for example
* ```csharp

View File

@@ -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))

View File

@@ -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))
}

View File

@@ -45,3 +45,6 @@ class C<T1, T2, T3, T4, T5, T6>
x1 = x15; // not a boxing conversion
}
}
// Ref structs can't be converted to a dynamic, object or valuetype.
ref struct S { }

View File

@@ -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

View File

@@ -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

View File

@@ -1,7 +1,5 @@
import csharp
from Struct s
where
s.fromSource() and
s.isRef()
from RefStruct s
where s.fromSource()
select s

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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<T>.M12() |
| ViableCallable.cs:588:9:588:15 | call to method M13 | I3<T>.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() |

View File

@@ -588,3 +588,33 @@ public class C20 : I3<C20>
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 t) where T : I
{
// Viable callable: A1.M()
t.M();
}
public void Run2<T>(T t) where T : I, allows ref struct
{
// Viable callable: {A1, A2}.M()
t.M();
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
public class TestClass
{
public void M1<T1>(T1 x) where T1 : class { }
public void M2<T2>(T2 x) where T2 : struct { }
public void M3<T3>(T3 x) where T3 : unmanaged { }
public void M4<T4>(T4 x) where T4 : new() { }
public void M5<T5>(T5 x) where T5 : notnull { }
public void M6<T6>(T6 x) where T6 : IList<object> { }
public void M7<T7>(T7 x) where T7 : allows ref struct { }
}

View File

@@ -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<object> |
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: ... |

View File

@@ -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()
}

View File

@@ -48,3 +48,11 @@ class Nested<T10>
Nested<int>.NestedB.NestedC<bool> x5;
Nested<string>.NestedB.NestedC<decimal> x6;
}
interface I2 { }
struct S3 : I2 { }
ref struct RS : I2 { }
class C7 : I2 { }
class NormalConstraint<T> where T : I2 { }
class NegativeConstraint<T> where T : I2, allows ref struct { }

View File

@@ -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<C0> |
@@ -57,6 +58,10 @@ constrainedTypeParameterSubsumes
| Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested<System.String>+NestedB+NestedC<T12> |
| Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested<System.String>+NestedB+NestedC<decimal> |
| Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC<T12> |
| 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<T> |
| Unification.cs:9:10:9:11 | T3 | Unification.cs:58:7:58:27 | NegativeConstraint<T> |
| Unification.cs:10:10:10:11 | T4 | Unification.cs:7:7:7:12 | C1<C0> |
| 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<S1> |
@@ -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<S2> |
| 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<C0> |
@@ -158,6 +176,10 @@ constrainedTypeParameterUnifiable
| Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested<System.String>+NestedB+NestedC<T12> |
| Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested<System.String>+NestedB+NestedC<decimal> |
| Unification.cs:9:10:9:11 | T3 | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC<T12> |
| 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<T> |
| Unification.cs:9:10:9:11 | T3 | Unification.cs:58:7:58:27 | NegativeConstraint<T> |
| Unification.cs:10:10:10:11 | T4 | Unification.cs:7:7:7:12 | C1<C0> |
| Unification.cs:10:10:10:11 | T4 | Unification.cs:7:7:7:12 | C1<T1> |
| Unification.cs:10:10:10:11 | T4 | Unification.cs:7:7:7:12 | C1<T2> |
@@ -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<S2> |
| Unification.cs:24:12:24:13 | Tm | Unification.cs:8:7:8:12 | C2<T2> |
| 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<C0> | Unification.cs:7:7:7:12 | C1<C0> |
| Unification.cs:7:7:7:12 | C1<S1> | Unification.cs:7:7:7:12 | C1<S1> |
@@ -312,6 +346,8 @@ subsumes
| Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC<T12> | Unification.cs:41:22:41:33 | Nested<System.String>+NestedB+NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC<T12> | Unification.cs:41:22:41:33 | Nested<System.String>+NestedB+NestedC<decimal> |
| Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC<T12> | Unification.cs:41:22:41:33 | Nested`1+NestedB+NestedC<T12> |
| Unification.cs:57:7:57:25 | NormalConstraint<T> | Unification.cs:57:7:57:25 | NormalConstraint<T> |
| Unification.cs:58:7:58:27 | NegativeConstraint<T> | Unification.cs:58:7:58:27 | NegativeConstraint<T> |
subsumptionImpliesUnification
unifiable
| Unification.cs:7:7:7:12 | C1<C0> | Unification.cs:7:7:7:12 | C1<T1> |