Skip to content

Commit cb99d5c

Browse files
committed
Swift: QL proto-type implementation of type inference
1 parent 3a1b761 commit cb99d5c

14 files changed

Lines changed: 5824 additions & 45 deletions

File tree

shared/typeinference/codeql/typeinference/internal/TypeInference.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,11 +2282,13 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
22822282
* A position where a callable can have a declared type and a call can have
22832283
* an inferred type.
22842284
*/
2285+
bindingset[this]
22852286
class TypePosition {
22862287
/** Holds if this position represents the return type of a callable. */
22872288
predicate isReturn();
22882289

22892290
/** Gets a textual representation of this position. */
2291+
bindingset[this]
22902292
string toString();
22912293
}
22922294

@@ -2717,6 +2719,8 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
27172719

27182720
class AccessPosition = DeclarationPosition;
27192721

2722+
bindingset[apos]
2723+
bindingset[dpos]
27202724
predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) {
27212725
apos = dpos
27222726
}

swift/ql/lib/codeql/swift/elements/internal/LocationImpl.qll

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,75 @@ module Impl {
55
* A location of a program element.
66
*/
77
class Location extends Generated::Location {
8+
/** Holds if this location starts before location `that`. */
9+
pragma[inline]
10+
predicate startsBefore(Location that) {
11+
exists(string f, int sl1, int sc1, int sl2, int sc2 |
12+
this.hasLocationInfo(f, sl1, sc1, _, _) and
13+
that.hasLocationInfo(f, sl2, sc2, _, _)
14+
|
15+
sl1 < sl2
16+
or
17+
sl1 = sl2 and sc1 <= sc2
18+
)
19+
}
20+
21+
/** Holds if this location starts strictly before location `that`. */
22+
pragma[inline]
23+
predicate startsStrictlyBefore(Location that) {
24+
exists(string f, int sl1, int sc1, int sl2, int sc2 |
25+
this.hasLocationInfo(f, sl1, sc1, _, _) and
26+
that.hasLocationInfo(f, sl2, sc2, _, _)
27+
|
28+
sl1 < sl2
29+
or
30+
sl1 = sl2 and sc1 < sc2
31+
)
32+
}
33+
34+
/** Holds if this location ends after location `that`. */
35+
pragma[inline]
36+
predicate endsAfter(Location that) {
37+
exists(string f, int el1, int ec1, int el2, int ec2 |
38+
this.hasLocationInfo(f, _, _, el1, ec1) and
39+
that.hasLocationInfo(f, _, _, el2, ec2)
40+
|
41+
el1 > el2
42+
or
43+
el1 = el2 and ec1 >= ec2
44+
)
45+
}
46+
47+
/** Holds if this location ends strictly after location `that`. */
48+
pragma[inline]
49+
predicate endsStrictlyAfter(Location that) {
50+
exists(string f, int el1, int ec1, int el2, int ec2 |
51+
this.hasLocationInfo(f, _, _, el1, ec1) and
52+
that.hasLocationInfo(f, _, _, el2, ec2)
53+
|
54+
el1 > el2
55+
or
56+
el1 = el2 and ec1 > ec2
57+
)
58+
}
59+
60+
/**
61+
* Holds if this location contains location `that`, meaning that it starts
62+
* before and ends after it.
63+
*/
64+
bindingset[this, that]
65+
pragma[inline_late]
66+
predicate contains(Location that) { this.startsBefore(that) and this.endsAfter(that) }
67+
68+
/**
69+
* Holds if this location strictlycontains location `that`, meaning that it starts
70+
* strictly before and ends strictly after it.
71+
*/
72+
pragma[inline]
73+
predicate strictlyContains(Location that) {
74+
this.startsStrictlyBefore(that) and this.endsStrictlyAfter(that)
75+
}
76+
877
/**
978
* Holds if this location is described by `path`, `startLine`, `startColumn`, `endLine` and `endColumn`.
1079
*/
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/** Provides classes representing types without type arguments. */
2+
3+
import swift
4+
private import swift as Swift
5+
private import codeql.swift.generated.Raw
6+
private import codeql.swift.generated.Synth
7+
8+
cached
9+
newtype TType =
10+
TTypeDeclType(TypeDecl decl) {
11+
not decl instanceof AssociatedTypeDecl and
12+
// handled in `TSelfTypeParameter`
13+
not decl = any(GenericTypeParamDecl g | decl.getName() = "Self")
14+
} or
15+
TTupleType(int arity) { arity = [0 .. any(Swift::TupleType tt).getNumberOfTypes()] } or
16+
TAssociatedTypeTypeParameter(ProtocolDecl protocol, AssociatedTypeDecl associatedType) {
17+
associatedType.getDeclaringDecl().(ProtocolDecl).getABaseTypeDecl*() = protocol
18+
} or
19+
TSelfTypeParameter(ProtocolDecl protocol) or
20+
TTupleTypeTypeParameter(int arity, int index) {
21+
arity = [0 .. any(Swift::TupleType tt).getNumberOfTypes()] and
22+
index = [0 .. arity - 1]
23+
} or
24+
TUnknownType()
25+
26+
/**
27+
* A type without type arguments.
28+
*
29+
* Note that this type includes things that, strictly speaking, are not Rust
30+
* types, such as traits and implementation blocks.
31+
*/
32+
abstract class Type extends TType {
33+
/**
34+
* Gets the `i`th positional type parameter of this type, if any.
35+
*
36+
* This excludes synthetic type parameters, such as associated types in traits.
37+
*/
38+
abstract TypeParameter getPositionalTypeParameter(int i);
39+
40+
/** Gets the default type for the `i`th type parameter, if any. */
41+
TypeRepr getTypeParameterDefault(int i) { none() }
42+
43+
/**
44+
* Gets a type parameter of this type.
45+
*
46+
* This includes both positional type parameters and synthetic type parameters,
47+
* such as associated types in traits.
48+
*/
49+
TypeParameter getATypeParameter() { result = this.getPositionalTypeParameter(_) }
50+
51+
/** Gets a textual representation of this type. */
52+
abstract string toString();
53+
54+
/** Gets the location of this type. */
55+
abstract Location getLocation();
56+
}
57+
58+
class TypeDeclType extends Type, TTypeDeclType {
59+
TypeDecl decl;
60+
61+
TypeDeclType() { this = TTypeDeclType(decl) }
62+
63+
/** Gets the type item that this data type represents. */
64+
TypeDecl getDecl() { result = decl }
65+
66+
override TypeParameter getPositionalTypeParameter(int i) {
67+
result = TTypeDeclType(decl.(GenericTypeDecl).getGenericTypeParam(i))
68+
}
69+
70+
override TypeParameter getATypeParameter() {
71+
result = super.getATypeParameter()
72+
or
73+
result = TAssociatedTypeTypeParameter(decl, _)
74+
}
75+
76+
override TypeRepr getTypeParameterDefault(int i) {
77+
// todo: it appears Swift does support this
78+
none()
79+
}
80+
81+
override string toString() { result = decl.getName() }
82+
83+
override Location getLocation() { result = decl.getLocation() }
84+
}
85+
86+
class StringType extends TypeDeclType {
87+
StringType() { decl.getName() = "String" }
88+
}
89+
90+
class IntType extends TypeDeclType {
91+
IntType() { decl.getName() = "Int" }
92+
}
93+
94+
class TupleType extends Type, TTupleType {
95+
int arity;
96+
97+
TupleType() { this = TTupleType(arity) }
98+
99+
override TypeParameter getPositionalTypeParameter(int i) {
100+
result = TTupleTypeTypeParameter(arity, i)
101+
}
102+
103+
override string toString() { result = "(T_" + arity + ")" }
104+
105+
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
106+
}
107+
108+
class VoidType extends TupleType {
109+
VoidType() { arity = 0 }
110+
}
111+
112+
/** A type parameter. */
113+
abstract class TypeParameter extends Type {
114+
override TypeParameter getPositionalTypeParameter(int i) { none() }
115+
}
116+
117+
class GenericTypeParamDeclTypeParameter extends TypeParameter, TypeDeclType {
118+
override GenericTypeParamDecl decl;
119+
120+
override TypeParameter getPositionalTypeParameter(int i) {
121+
result = TypeParameter.super.getPositionalTypeParameter(i)
122+
}
123+
}
124+
125+
/**
126+
* A type parameter corresponding to an associated type in a protocol.
127+
*
128+
* We treat associated type declarations in protocols as type parameters. E.g., a
129+
* protocol such as
130+
*
131+
* ```swift
132+
* protocol Protocol {
133+
* associatedtype AssociatedType
134+
* // ...
135+
* }
136+
* ```
137+
*
138+
* is treated as if it was
139+
*
140+
* ```swift
141+
* protocol Protocol<AssociatedType> {
142+
* // ...
143+
* }
144+
* ```
145+
*
146+
* Furthermore, associated types of a super protocol induce a corresponding type
147+
* parameter in any subprotocols. E.g., if we have a protocol `SubProtocol: AProtocol` then
148+
* `SubProtocol` also has a type parameter for the associated type
149+
* `AssociatedType`.
150+
*/
151+
class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypeParameter {
152+
ProtocolDecl protocol;
153+
AssociatedTypeDecl associatedType;
154+
155+
AssociatedTypeTypeParameter() { this = TAssociatedTypeTypeParameter(protocol, associatedType) }
156+
157+
AssociatedTypeDecl getAssociatedType() { result = associatedType }
158+
159+
/** Gets the protocol that contains this associated type declaration. */
160+
ProtocolDecl getProtocol() { result = protocol }
161+
162+
/**
163+
* Holds if this associated type type parameter corresponds directly its
164+
* protocol, that is, it is not inherited from a superprotocol.
165+
*/
166+
predicate isDirect() { protocol = associatedType.getDeclaringDecl() }
167+
168+
override string toString() {
169+
exists(string fromString, ProtocolDecl protocol2 |
170+
result = associatedType.getName() + "[" + protocol.getName() + fromString + "]" and
171+
protocol2 = associatedType.getDeclaringDecl() and
172+
if protocol = protocol2
173+
then fromString = ""
174+
else fromString = " (inherited from " + protocol2.getName() + ")"
175+
)
176+
}
177+
178+
override Location getLocation() { result = associatedType.getLocation() }
179+
180+
override TypeParameter getPositionalTypeParameter(int i) {
181+
// todo: it appears that associated types can also have type parameters
182+
result = TypeParameter.super.getPositionalTypeParameter(i)
183+
}
184+
}
185+
186+
/**
187+
* The implicit `Self` type parameter of a protocol, that refers to the
188+
* implementing type of the protocol.
189+
*/
190+
class SelfTypeParameter extends TypeParameter, TSelfTypeParameter {
191+
private ProtocolDecl protocol;
192+
193+
SelfTypeParameter() { this = TSelfTypeParameter(protocol) }
194+
195+
ProtocolDecl getProtocol() { result = protocol }
196+
197+
override string toString() { result = "Self [" + protocol.getName() + "]" }
198+
199+
override Location getLocation() { result = protocol.getLocation() }
200+
}
201+
202+
class TupleTypeTypeParameter extends TypeParameter, TTupleTypeTypeParameter {
203+
int arity;
204+
int index;
205+
206+
TupleTypeTypeParameter() { this = TTupleTypeTypeParameter(arity, index) }
207+
208+
override string toString() { result = "element " + index + " of tuple of arity " + arity }
209+
210+
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
211+
}
212+
213+
/**
214+
* A special pseudo type used to indicate that the actual type may have to be
215+
* inferred by propagating type information back into call arguments.
216+
*
217+
* For example, in
218+
*
219+
* ```rust
220+
* let x = Default::default();
221+
* foo(x);
222+
* ```
223+
*
224+
* `Default::default()` is assigned this type, which allows us to infer the actual
225+
* type from the type of `foo`'s first parameter.
226+
*
227+
* Unknown types are not restricted to root types, for example in a call like
228+
* `Vec::new()` we assign this type at the type path corresponding to the type
229+
* parameter of `Vec`.
230+
*
231+
* Unknown types are used to restrict when type information is allowed to flow
232+
* into call arguments (including method call receivers), in order to avoid
233+
* combinatorial explosions.
234+
*/
235+
class UnknownType extends Type, TUnknownType {
236+
override TypeParameter getPositionalTypeParameter(int i) { none() }
237+
238+
override string toString() { result = "(context typed)" }
239+
240+
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
241+
}
242+
243+
private class RawTypeParameter =
244+
@generic_type_param_decl or @associated_type_decl or @protocol_decl;
245+
246+
private predicate id(RawTypeParameter x, RawTypeParameter y) { x = y }
247+
248+
private predicate idOfRaw(RawTypeParameter x, int y) = equivalenceRelation(id/2)(x, y)
249+
250+
int idOfTypeParameterAstNode(AstNode node) { idOfRaw(Synth::convertAstNodeToRaw(node), result) }
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
private import Type
2+
3+
/**
4+
* A type abstraction. I.e., a place in the program where type variables may
5+
* be introduced.
6+
*
7+
* Example:
8+
* ```rust
9+
* impl<A, B> Foo<A, B> { }
10+
* // ^^^^^^ a type abstraction
11+
* ```
12+
*/
13+
abstract class TypeAbstraction extends AstNode {
14+
abstract TypeParameter getATypeParameter();
15+
}
16+
17+
final class GenericContextTypeAbstraction extends TypeAbstraction, GenericContext {
18+
override TypeParameter getATypeParameter() {
19+
result.(GenericTypeParamDeclTypeParameter).getDecl() = this.getAGenericTypeParam()
20+
or
21+
result.(SelfTypeParameter).getProtocol() = this
22+
or
23+
result.(AssociatedTypeTypeParameter).getProtocol() = this
24+
}
25+
}

0 commit comments

Comments
 (0)