Skip to content

Commit

Permalink
C#: Improve unification logic to handle ref structs.
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelnebel committed Jan 3, 2025
1 parent a358617 commit 00e62a1
Showing 1 changed file with 29 additions and 9 deletions.
38 changes: 29 additions & 9 deletions csharp/ql/lib/semmle/code/csharp/Unification.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -559,29 +564,31 @@ 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
ttc = TValueTypeConstraint() and
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
ttc = TValueTypeConstraint() and
t.isValueType()
or
typeConstraintSubsumes(ttc, t)
)
) and
(t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint())
}
}

Expand All @@ -603,7 +610,8 @@ module Unification {
t.isValueType()
or
typeConstraintUnifiable(ttc, t)
)
) and
(t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint())
}

bindingset[this]
Expand All @@ -617,7 +625,8 @@ module Unification {
t.isValueType()
or
typeConstraintSubsumes(ttc, t)
)
) and
(t.isRefLikeType() implies getANegativeTypeConstraint(this) = TAllowRefTypeConstraint())
}
}

Expand All @@ -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() |
Expand All @@ -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))
Expand Down

0 comments on commit 00e62a1

Please sign in to comment.