C#: Teach unification library about nested types

This commit is contained in:
Tom Hvitved
2020-04-24 14:59:13 +02:00
parent 851fc98b01
commit 4c1a9b25c1
3 changed files with 174 additions and 48 deletions

View File

@@ -10,9 +10,69 @@ private import Caching
* equal modulo identity conversions and type parameters.
*/
module Gvn {
/** Gets the qualified name of type `t`. */
string getQualifiedName(Type t) {
if not t instanceof NestedType or t.(NestedType).getDeclaringType() instanceof GenericType
then result = t.getName()
else result = getQualifiedName(t.(NestedType).getDeclaringType()) + "." + t.getName()
}
/**
* A generic type. This is either a type with a type parameter, a type with
* a type argument, or a nested type with a generic enclosing type.
*/
class GenericType extends Type {
GenericType() {
exists(this.getChild(0))
or
this.(NestedType).getDeclaringType() instanceof GenericType
}
/** Gets the generic containing type, if any. */
GenericType getQualifier() { result = this.(NestedType).getDeclaringType() }
/**
* Gets the number of children of the generic containing type, or 0 if there
* is no generic containing type.
*/
int getNumberOfQualifierChildrenExt() {
result = this.getQualifier().getNumberOfChildrenExt()
or
not exists(this.getQualifier()) and result = 0
}
/** Gets the number of children of this type, not taking nested types into account. */
int getNumberOfChildrenSelf() { result = count(int i | exists(this.getChild(i)) and i >= 0) }
/** Gets the number of children of this type, taking nested types into account. */
int getNumberOfChildrenExt() {
result = this.getNumberOfQualifierChildrenExt() + this.getNumberOfChildrenSelf()
}
/** Gets the `i`th child of this type, taking nested types into account. */
Type getChildExt(int i) {
result = this.getQualifier().getChildExt(i)
or
exists(int offset |
offset = this.getNumberOfQualifierChildrenExt() and
result = this.getChild(i - offset) and
i >= offset
)
}
/** Gets a textual representation of this type, taking nested types into account. */
string toStringExt() {
exists(string name | name = getQualifiedName(this) |
result = this.getQualifier().toStringExt() + "." + name
or
not exists(this.getQualifier()) and result = name
)
}
}
private class LeafType extends Type {
LeafType() {
not exists(this.getAChild()) and
not this instanceof GenericType and
not this instanceof TypeParameter and
not this instanceof DynamicType
}
@@ -28,14 +88,22 @@ module Gvn {
or
this = TArrayTypeKind(_, _) and result = 1
or
exists(UnboundGenericType ugt | this = TConstructedType(ugt) |
result = ugt.getNumberOfTypeParameters()
exists(GenericType t | this = TConstructedType(t.getSourceDeclaration()) |
result = t.getNumberOfChildrenExt()
)
}
/** Gets a textual representation of this kind when applied to arguments `args`. */
/** Gets the source declaration type that this kind corresponds to, if any. */
GenericType getConstructedSourceDeclaration() { this = TConstructedType(result) }
/**
* Gets a textual representation of this kind when applied to arguments `args`.
*
* This predicate is restricted to built-in generics (pointers, nullables, and
* arrays).
*/
bindingset[args]
string toString(string args) {
string toStringBuiltin(string args) {
this = TPointerTypeKind() and result = args + "*"
or
this = TNullableTypeKind() and result = args + "?"
@@ -43,14 +111,14 @@ module Gvn {
exists(int rnk | this = TArrayTypeKind(_, rnk) |
result = args + "[" + concat(int i | i in [0 .. rnk - 2] | ",") + "]"
)
or
exists(UnboundGenericType ugt | this = TConstructedType(ugt) |
result = ugt.getNameWithoutBrackets() + "<" + args + ">"
)
}
/** Gets a textual representation of this kind. */
string toString() { result = toString("") }
string toString() {
result = this.toStringBuiltin("")
or
result = this.getConstructedSourceDeclaration().toStringExt()
}
/** Gets the location of this kind. */
Location getLocation() { result instanceof EmptyLocation }
@@ -64,11 +132,9 @@ module Gvn {
or
t = any(ArrayType at | result = TArrayTypeKind(at.getDimension(), at.getRank()))
or
result = TConstructedType(t.(ConstructedType).getUnboundGeneric())
result = TConstructedType(t.getSourceDeclaration())
or
result = TConstructedType(t.(TupleType).getUnderlyingType().getUnboundGeneric())
or
result = TConstructedType(t)
result = TConstructedType(t.(TupleType).getUnderlyingType().getSourceDeclaration())
}
/**
@@ -107,7 +173,7 @@ module Gvn {
override CompoundTypeKind getKind() { result = l.getKind() }
}
private ConstructedGvnTypeList gvnConstructed(Type t, CompoundTypeKind k, int i) {
private ConstructedGvnTypeList gvnConstructed(GenericType t, CompoundTypeKind k, int i) {
result = TConstructedGvnTypeNil(k) and
i = -1 and
k = getTypeKind(t)
@@ -118,14 +184,16 @@ module Gvn {
}
pragma[noinline]
private GvnType gvnTypeChild(Type t, int i) { result = getGlobalValueNumber(t.getChild(i)) }
private GvnType gvnTypeChildExt(GenericType t, int i) {
result = getGlobalValueNumber(t.getChildExt(i))
}
pragma[noinline]
private predicate gvnConstructedCons(
Type t, CompoundTypeKind k, int i, GvnType head, ConstructedGvnTypeList tail
GenericType t, CompoundTypeKind k, int i, GvnType head, ConstructedGvnTypeList tail
) {
tail = gvnConstructed(t, k, i - 1) and
head = gvnTypeChild(t, i)
head = gvnTypeChildExt(t, i)
}
private class ConstructedGvnTypeList extends TConstructedGvnTypeList {
@@ -150,17 +218,47 @@ module Gvn {
)
}
/**
* Gets a textual representation of this constructed type, restricted
* to the prefix `t` of the underlying source declaration type.
*
* The `toString()` calculation needs to be split up into prefixes, in
* order to apply the type arguments correctly. For example, a source
* declaration type `A<>.B.C<,>` applied to types `int, string, bool`
* needs to be printed as `A<int>.B.C<string,bool>`.
*/
language[monotonicAggregates]
private string toStringConstructed(GenericType t) {
t = this.getKind().getConstructedSourceDeclaration().getQualifier*() and
exists(int offset, int children, string name, string nameArgs |
offset = t.getNumberOfQualifierChildrenExt() and
children = t.getNumberOfChildrenSelf() and
name = getQualifiedName(t) and
if children = 0
then nameArgs = name
else
exists(string offsetArgs |
offsetArgs =
concat(int i |
i in [offset .. offset + children - 1]
|
this.getArg(i).toString(), "," order by i
) and
nameArgs = name.prefix(name.length() - children - 1) + "<" + offsetArgs + ">"
)
|
offset = 0 and result = nameArgs
or
result = this.toStringConstructed(t.getQualifier()) + "." + nameArgs
)
}
language[monotonicAggregates]
string toString() {
exists(CompoundTypeKind k, string args |
k = this.getKind() and
args =
concat(int i |
i in [0 .. k.getNumberOfTypeParameters() - 1]
|
this.getArg(i).toString(), "," order by i
) and
result = k.toString(args)
exists(CompoundTypeKind k | k = this.getKind() |
result = k.toStringBuiltin(this.getArg(0).toString())
or
result = this.toStringConstructed(k.getConstructedSourceDeclaration())
)
}
@@ -366,7 +464,12 @@ module Gvn {
TArrayTypeKind(int dim, int rnk) {
exists(ArrayType at | dim = at.getDimension() and rnk = at.getRank())
} or
TConstructedType(UnboundGenericType ugt) { exists(ugt.getATypeParameter()) }
TConstructedType(GenericType sourceDecl) {
sourceDecl = any(GenericType t).getSourceDeclaration() and
not sourceDecl instanceof PointerType and
not sourceDecl instanceof NullableType and
not sourceDecl instanceof ArrayType
}
cached
newtype TGvnType =

View File

@@ -32,17 +32,17 @@ edges
| Types.cs:74:9:74:9 | access to local variable d : D | Types.cs:16:30:16:30 | this : D |
| Types.cs:77:22:77:22 | a : C | Types.cs:79:18:79:25 | SSA def(b) : C |
| Types.cs:79:18:79:25 | SSA def(b) : C | Types.cs:80:18:80:18 | access to local variable b |
| Types.cs:90:22:90:22 | e : E2 | Types.cs:92:26:92:26 | access to parameter e : E2 |
| Types.cs:92:13:92:16 | [post] this access [Field] : E2 | Types.cs:93:13:93:16 | this access [Field] : E2 |
| Types.cs:92:26:92:26 | access to parameter e : E2 | Types.cs:92:13:92:16 | [post] this access [Field] : E2 |
| Types.cs:93:13:93:16 | this access [Field] : E2 | Types.cs:113:34:113:34 | this [Field] : E2 |
| Types.cs:110:25:110:32 | object creation of type E2 : E2 | Types.cs:90:22:90:22 | e : E2 |
| Types.cs:113:34:113:34 | this [Field] : E2 | Types.cs:115:22:115:25 | this access [Field] : E2 |
| Types.cs:115:22:115:25 | this access [Field] : E2 | Types.cs:115:22:115:31 | access to field Field |
| Types.cs:90:22:90:22 | e : Types.E<D>.E2 | Types.cs:92:26:92:26 | access to parameter e : Types.E<D>.E2 |
| Types.cs:92:13:92:16 | [post] this access [Field] : Types.E<D>.E2 | Types.cs:93:13:93:16 | this access [Field] : Types.E<D>.E2 |
| Types.cs:92:26:92:26 | access to parameter e : Types.E<D>.E2 | Types.cs:92:13:92:16 | [post] this access [Field] : Types.E<D>.E2 |
| Types.cs:93:13:93:16 | this access [Field] : Types.E<D>.E2 | Types.cs:113:34:113:34 | this [Field] : Types.E<D>.E2 |
| Types.cs:110:25:110:32 | object creation of type E2 : Types.E<D>.E2 | Types.cs:90:22:90:22 | e : Types.E<D>.E2 |
| Types.cs:113:34:113:34 | this [Field] : Types.E<D>.E2 | Types.cs:115:22:115:25 | this access [Field] : Types.E<D>.E2 |
| Types.cs:115:22:115:25 | this access [Field] : Types.E<D>.E2 | Types.cs:115:22:115:31 | access to field Field |
| Types.cs:120:25:120:31 | object creation of type A : A | Types.cs:122:30:122:30 | access to local variable a : A |
| Types.cs:121:26:121:33 | object creation of type E2 : E2 | Types.cs:123:30:123:31 | access to local variable e2 : E2 |
| Types.cs:121:26:121:33 | object creation of type E2 : Types.E<D>.E2 | Types.cs:123:30:123:31 | access to local variable e2 : Types.E<D>.E2 |
| Types.cs:122:30:122:30 | access to local variable a : A | Types.cs:122:22:122:31 | call to method Through |
| Types.cs:123:30:123:31 | access to local variable e2 : E2 | Types.cs:123:22:123:32 | call to method Through |
| Types.cs:123:30:123:31 | access to local variable e2 : Types.E<D>.E2 | Types.cs:123:22:123:32 | call to method Through |
nodes
| Types.cs:7:21:7:25 | this : D | semmle.label | this : D |
| Types.cs:7:32:7:35 | this access : D | semmle.label | this access : D |
@@ -86,20 +86,20 @@ nodes
| Types.cs:77:22:77:22 | a : C | semmle.label | a : C |
| Types.cs:79:18:79:25 | SSA def(b) : C | semmle.label | SSA def(b) : C |
| Types.cs:80:18:80:18 | access to local variable b | semmle.label | access to local variable b |
| Types.cs:90:22:90:22 | e : E2 | semmle.label | e : E2 |
| Types.cs:92:13:92:16 | [post] this access [Field] : E2 | semmle.label | [post] this access [Field] : E2 |
| Types.cs:92:26:92:26 | access to parameter e : E2 | semmle.label | access to parameter e : E2 |
| Types.cs:93:13:93:16 | this access [Field] : E2 | semmle.label | this access [Field] : E2 |
| Types.cs:110:25:110:32 | object creation of type E2 : E2 | semmle.label | object creation of type E2 : E2 |
| Types.cs:113:34:113:34 | this [Field] : E2 | semmle.label | this [Field] : E2 |
| Types.cs:115:22:115:25 | this access [Field] : E2 | semmle.label | this access [Field] : E2 |
| Types.cs:90:22:90:22 | e : Types.E<D>.E2 | semmle.label | e : Types.E<D>.E2 |
| Types.cs:92:13:92:16 | [post] this access [Field] : Types.E<D>.E2 | semmle.label | [post] this access [Field] : Types.E<D>.E2 |
| Types.cs:92:26:92:26 | access to parameter e : Types.E<D>.E2 | semmle.label | access to parameter e : Types.E<D>.E2 |
| Types.cs:93:13:93:16 | this access [Field] : Types.E<D>.E2 | semmle.label | this access [Field] : Types.E<D>.E2 |
| Types.cs:110:25:110:32 | object creation of type E2 : Types.E<D>.E2 | semmle.label | object creation of type E2 : Types.E<D>.E2 |
| Types.cs:113:34:113:34 | this [Field] : Types.E<D>.E2 | semmle.label | this [Field] : Types.E<D>.E2 |
| Types.cs:115:22:115:25 | this access [Field] : Types.E<D>.E2 | semmle.label | this access [Field] : Types.E<D>.E2 |
| Types.cs:115:22:115:31 | access to field Field | semmle.label | access to field Field |
| Types.cs:120:25:120:31 | object creation of type A : A | semmle.label | object creation of type A : A |
| Types.cs:121:26:121:33 | object creation of type E2 : E2 | semmle.label | object creation of type E2 : E2 |
| Types.cs:121:26:121:33 | object creation of type E2 : Types.E<D>.E2 | semmle.label | object creation of type E2 : Types.E<D>.E2 |
| Types.cs:122:22:122:31 | call to method Through | semmle.label | call to method Through |
| Types.cs:122:30:122:30 | access to local variable a : A | semmle.label | access to local variable a : A |
| Types.cs:123:22:123:32 | call to method Through | semmle.label | call to method Through |
| Types.cs:123:30:123:31 | access to local variable e2 : E2 | semmle.label | access to local variable e2 : E2 |
| Types.cs:123:30:123:31 | access to local variable e2 : Types.E<D>.E2 | semmle.label | access to local variable e2 : Types.E<D>.E2 |
#select
| Types.cs:23:12:23:18 | object creation of type C : C | Types.cs:50:18:50:18 | access to local variable c | Types.cs:50:18:50:18 | access to local variable c | $@ | Types.cs:50:18:50:18 | access to local variable c | access to local variable c |
| Types.cs:25:12:25:18 | object creation of type C : C | Types.cs:63:33:63:36 | (...) ... | Types.cs:63:33:63:36 | (...) ... | $@ | Types.cs:63:33:63:36 | (...) ... | (...) ... |
@@ -115,6 +115,6 @@ nodes
| Types.cs:39:12:39:18 | object creation of type D : D | Types.cs:69:52:69:52 | access to parameter x | Types.cs:69:52:69:52 | access to parameter x | $@ | Types.cs:69:52:69:52 | access to parameter x | access to parameter x |
| Types.cs:40:12:40:18 | object creation of type D : D | Types.cs:16:42:16:45 | this access | Types.cs:16:42:16:45 | this access | $@ | Types.cs:16:42:16:45 | this access | this access |
| Types.cs:43:20:43:23 | null : null | Types.cs:44:14:44:14 | access to local variable o | Types.cs:44:14:44:14 | access to local variable o | $@ | Types.cs:44:14:44:14 | access to local variable o | access to local variable o |
| Types.cs:110:25:110:32 | object creation of type E2 : E2 | Types.cs:115:22:115:31 | access to field Field | Types.cs:115:22:115:31 | access to field Field | $@ | Types.cs:115:22:115:31 | access to field Field | access to field Field |
| Types.cs:110:25:110:32 | object creation of type E2 : Types.E<D>.E2 | Types.cs:115:22:115:31 | access to field Field | Types.cs:115:22:115:31 | access to field Field | $@ | Types.cs:115:22:115:31 | access to field Field | access to field Field |
| Types.cs:120:25:120:31 | object creation of type A : A | Types.cs:122:22:122:31 | call to method Through | Types.cs:122:22:122:31 | call to method Through | $@ | Types.cs:122:22:122:31 | call to method Through | call to method Through |
| Types.cs:121:26:121:33 | object creation of type E2 : E2 | Types.cs:123:22:123:32 | call to method Through | Types.cs:123:22:123:32 | call to method Through | $@ | Types.cs:123:22:123:32 | call to method Through | call to method Through |
| Types.cs:121:26:121:33 | object creation of type E2 : Types.E<D>.E2 | Types.cs:123:22:123:32 | call to method Through | Types.cs:123:22:123:32 | call to method Through | $@ | Types.cs:123:22:123:32 | call to method Through | call to method Through |

View File

@@ -296,13 +296,26 @@ subsumes
| Unification.cs:36:7:36:17 | Nested<T10> | Unification.cs:36:7:36:17 | Nested<String> |
| Unification.cs:36:7:36:17 | Nested<T10> | Unification.cs:36:7:36:17 | Nested<T10> |
| Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<String> |
| Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<String>.NestedA<Int32> |
| Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<String>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<String> | Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<String> |
| Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<String> |
| Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<String>.NestedA<Int32> | Unification.cs:38:11:38:22 | Nested<String>.NestedA<Int32> |
| Unification.cs:38:11:38:22 | Nested<String>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<String>.NestedA<Int32> |
| Unification.cs:38:11:38:22 | Nested<String>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<String>.NestedA<T11> |
| Unification.cs:39:11:39:17 | Nested<>.NestedB | Unification.cs:39:11:39:17 | Nested<>.NestedB |
| Unification.cs:39:11:39:17 | Nested<>.NestedB | Unification.cs:39:11:39:17 | Nested<Int32>.NestedB |
| Unification.cs:39:11:39:17 | Nested<>.NestedB | Unification.cs:39:11:39:17 | Nested<String>.NestedB |
| Unification.cs:39:11:39:17 | Nested<Int32>.NestedB | Unification.cs:39:11:39:17 | Nested<Int32>.NestedB |
| Unification.cs:39:11:39:17 | Nested<String>.NestedB | Unification.cs:39:11:39:17 | Nested<String>.NestedB |
| Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<Boolean> |
| Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<String>.NestedB.NestedC<Decimal> |
| Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<String>.NestedB.NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<Boolean> | Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<Boolean> |
| Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<Boolean> |
| Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<T12> |
@@ -349,7 +362,17 @@ unifiable
| Unification.cs:31:12:31:23 | (string, T9) | Unification.cs:33:12:33:23 | (T8, T9) |
| Unification.cs:36:7:36:17 | Nested<Int32> | Unification.cs:36:7:36:17 | Nested<T10> |
| Unification.cs:36:7:36:17 | Nested<String> | Unification.cs:36:7:36:17 | Nested<T10> |
| Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<String> | Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<String> | Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<Int32>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<String>.NestedA<Int32> | Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<String>.NestedA<Int32> | Unification.cs:38:11:38:22 | Nested<String>.NestedA<T11> |
| Unification.cs:38:11:38:22 | Nested<String>.NestedA<T11> | Unification.cs:38:11:38:22 | Nested<>.NestedA<T11> |
| Unification.cs:39:11:39:17 | Nested<Int32>.NestedB | Unification.cs:39:11:39:17 | Nested<>.NestedB |
| Unification.cs:39:11:39:17 | Nested<String>.NestedB | Unification.cs:39:11:39:17 | Nested<>.NestedB |
| Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<Boolean> | Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<Boolean> | Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested<Int32>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested<String>.NestedB.NestedC<Decimal> | Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested<String>.NestedB.NestedC<Decimal> | Unification.cs:41:22:41:33 | Nested<String>.NestedB.NestedC<T12> |
| Unification.cs:41:22:41:33 | Nested<String>.NestedB.NestedC<T12> | Unification.cs:41:22:41:33 | Nested<>.NestedB.NestedC<T12> |