Rust: Address PR feedback

This commit is contained in:
Simon Friis Vindum
2025-05-19 15:35:40 +02:00
parent f4ff815253
commit 654d410485
5 changed files with 147 additions and 131 deletions

View File

@@ -335,7 +335,16 @@ class SelfTypeParameter extends TypeParameter, TSelfTypeParameter {
override Location getLocation() { result = trait.getLocation() }
}
/** A type abstraction. */
/**
* A type abstraction. I.e., a place in the program where type variables are
* introduced.
*
* Example:
* ```rust
* impl<A, B> Foo<A, B> { }
* // ^^^^^^ a type abstraction
* ```
*/
abstract class TypeAbstraction extends AstNode {
abstract TypeParameter getATypeParameter();
}

View File

@@ -19,16 +19,6 @@ private module Input1 implements InputSig1<Location> {
class TypeParameter = T::TypeParameter;
/**
* A type abstraction. I.e., a place in the program where type variables are
* introduced.
*
* Example:
* ```rust
* impl<A, B> Foo<A, B> { }
* // ^^^^^^ a type abstraction
* ```
*/
class TypeAbstraction = T::TypeAbstraction;
private newtype TTypeArgumentPosition =
@@ -156,7 +146,7 @@ private module Input2 implements InputSig2 {
exists(TypeParam param |
abs = param.getTypeBoundList().getABound() and
condition = param and
constraint = param.getTypeBoundList().getABound().getTypeRepr()
constraint = abs.(TypeBound).getTypeRepr()
)
or
// the implicit `Self` type parameter satisfies the trait
@@ -968,22 +958,6 @@ private module Cached {
)
}
pragma[nomagic]
private Type receiverRootType(Expr e) {
any(MethodCallExpr mce).getReceiver() = e and
result = inferType(e)
}
pragma[nomagic]
private Type inferReceiverType(Expr e, TypePath path) {
exists(Type root | root = receiverRootType(e) |
// for reference types, lookup members in the type being referenced
if root = TRefType()
then result = inferType(e, TypePath::cons(TRefTypeParameter(), path))
else result = inferType(e, path)
)
}
private class ReceiverExpr extends Expr {
MethodCallExpr mce;
@@ -993,13 +967,22 @@ private module Cached {
int getNumberOfArgs() { result = mce.getArgList().getNumberOfArgs() }
Type resolveTypeAt(TypePath path) { result = inferReceiverType(this, path) }
pragma[nomagic]
Type getTypeAt(TypePath path) {
exists(TypePath path0 | result = inferType(this, path0) |
path0 = TypePath::cons(TRefTypeParameter(), path)
or
not path0.isCons(TRefTypeParameter(), _) and
not (path0.isEmpty() and result = TRefType()) and
path = path0
)
}
}
/** Holds if a method for `type` with the name `name` and the arity `arity` exists in `impl`. */
pragma[nomagic]
private predicate methodCandidate(Type type, string name, int arity, Impl impl) {
type = impl.(ImplTypeAbstraction).getSelfTy().(TypeReprMention).resolveType() and
type = impl.getSelfTy().(TypeReprMention).resolveType() and
exists(Function f |
f = impl.(ImplItemNode).getASuccessor(name) and
f.getParamList().hasSelfParam() and
@@ -1009,17 +992,16 @@ private module Cached {
private module IsInstantiationOfInput implements IsInstantiationOfInputSig<ReceiverExpr> {
pragma[nomagic]
predicate potentialInstantiationOf(ReceiverExpr receiver, TypeAbstraction impl, TypeMention sub) {
methodCandidate(receiver.resolveTypeAt(TypePath::nil()), receiver.getField(),
predicate potentialInstantiationOf(
ReceiverExpr receiver, TypeAbstraction impl, TypeMention constraint
) {
methodCandidate(receiver.getTypeAt(TypePath::nil()), receiver.getField(),
receiver.getNumberOfArgs(), impl) and
sub = impl.(ImplTypeAbstraction).getSelfTy()
constraint = impl.(ImplTypeAbstraction).getSelfTy()
}
predicate relevantTypeMention(TypeMention sub) {
exists(TypeAbstraction impl |
methodCandidate(_, _, _, impl) and
sub = impl.(ImplTypeAbstraction).getSelfTy()
)
predicate relevantTypeMention(TypeMention constraint) {
exists(Impl impl | methodCandidate(_, _, _, impl) and constraint = impl.getSelfTy())
}
}
@@ -1044,8 +1026,7 @@ private module Cached {
*/
private Function getMethodFromImpl(ReceiverExpr receiver) {
exists(Impl impl |
IsInstantiationOf<ReceiverExpr, IsInstantiationOfInput>::isInstantiationOf(receiver, impl,
impl.(ImplTypeAbstraction).getSelfTy().(TypeReprMention)) and
IsInstantiationOf<ReceiverExpr, IsInstantiationOfInput>::isInstantiationOf(receiver, impl, _) and
result = getMethodSuccessor(impl, receiver.getField())
)
}
@@ -1059,19 +1040,17 @@ private module Cached {
or
// The type of `receiver` is a type parameter and the method comes from a
// trait bound on the type parameter.
result = getTypeParameterMethod(receiver.resolveTypeAt(TypePath::nil()), receiver.getField())
result = getTypeParameterMethod(receiver.getTypeAt(TypePath::nil()), receiver.getField())
)
}
pragma[inline]
private Type inferRootTypeDeref(AstNode n) {
exists(Type t |
t = inferType(n) and
// for reference types, lookup members in the type being referenced
if t = TRefType()
then result = inferType(n, TypePath::singleton(TRefTypeParameter()))
else result = t
)
result = inferType(n) and
result != TRefType()
or
// for reference types, lookup members in the type being referenced
result = inferType(n, TypePath::singleton(TRefTypeParameter()))
}
pragma[nomagic]

View File

@@ -1,6 +1,4 @@
testFailures
| main.rs:793:25:793:75 | //... | Missing result: type=a:A.S1 |
| main.rs:793:25:793:75 | //... | Missing result: type=a:MyThing |
inferType
| loop/main.rs:7:12:7:15 | SelfParam | | loop/main.rs:6:1:8:1 | Self [trait T1] |
| loop/main.rs:11:12:11:15 | SelfParam | | loop/main.rs:10:1:14:1 | Self [trait T2] |
@@ -798,10 +796,13 @@ inferType
| main.rs:788:9:788:9 | x | | main.rs:787:26:787:41 | T2 |
| main.rs:788:9:788:14 | x.m1() | | main.rs:787:22:787:23 | T1 |
| main.rs:791:56:791:56 | x | | main.rs:791:39:791:53 | T |
| main.rs:793:13:793:13 | a | | main.rs:741:20:741:22 | Tr2 |
| main.rs:793:13:793:13 | a | | main.rs:721:5:724:5 | MyThing |
| main.rs:793:13:793:13 | a | A | main.rs:731:5:732:14 | S1 |
| main.rs:793:17:793:17 | x | | main.rs:791:39:791:53 | T |
| main.rs:793:17:793:22 | x.m1() | | main.rs:741:20:741:22 | Tr2 |
| main.rs:794:26:794:26 | a | | main.rs:741:20:741:22 | Tr2 |
| main.rs:793:17:793:22 | x.m1() | | main.rs:721:5:724:5 | MyThing |
| main.rs:793:17:793:22 | x.m1() | A | main.rs:731:5:732:14 | S1 |
| main.rs:794:26:794:26 | a | | main.rs:721:5:724:5 | MyThing |
| main.rs:794:26:794:26 | a | A | main.rs:731:5:732:14 | S1 |
| main.rs:798:13:798:13 | x | | main.rs:721:5:724:5 | MyThing |
| main.rs:798:13:798:13 | x | A | main.rs:731:5:732:14 | S1 |
| main.rs:798:17:798:33 | MyThing {...} | | main.rs:721:5:724:5 | MyThing |
@@ -856,9 +857,7 @@ inferType
| main.rs:816:17:816:33 | MyThing {...} | A | main.rs:731:5:732:14 | S1 |
| main.rs:816:30:816:31 | S1 | | main.rs:731:5:732:14 | S1 |
| main.rs:817:13:817:13 | s | | main.rs:731:5:732:14 | S1 |
| main.rs:817:13:817:13 | s | | main.rs:741:20:741:22 | Tr2 |
| main.rs:817:17:817:32 | call_trait_m1(...) | | main.rs:731:5:732:14 | S1 |
| main.rs:817:17:817:32 | call_trait_m1(...) | | main.rs:741:20:741:22 | Tr2 |
| main.rs:817:31:817:31 | x | | main.rs:721:5:724:5 | MyThing |
| main.rs:817:31:817:31 | x | A | main.rs:731:5:732:14 | S1 |
| main.rs:819:13:819:13 | x | | main.rs:726:5:729:5 | MyThing2 |
@@ -867,10 +866,8 @@ inferType
| main.rs:819:17:819:34 | MyThing2 {...} | A | main.rs:733:5:734:14 | S2 |
| main.rs:819:31:819:32 | S2 | | main.rs:733:5:734:14 | S2 |
| main.rs:820:13:820:13 | s | | main.rs:721:5:724:5 | MyThing |
| main.rs:820:13:820:13 | s | | main.rs:741:20:741:22 | Tr2 |
| main.rs:820:13:820:13 | s | A | main.rs:733:5:734:14 | S2 |
| main.rs:820:17:820:32 | call_trait_m1(...) | | main.rs:721:5:724:5 | MyThing |
| main.rs:820:17:820:32 | call_trait_m1(...) | | main.rs:741:20:741:22 | Tr2 |
| main.rs:820:17:820:32 | call_trait_m1(...) | A | main.rs:733:5:734:14 | S2 |
| main.rs:820:31:820:31 | x | | main.rs:726:5:729:5 | MyThing2 |
| main.rs:820:31:820:31 | x | A | main.rs:733:5:734:14 | S2 |

View File

@@ -237,14 +237,20 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
TypePath cons(TypeParameter tp, TypePath suffix) { result = singleton(tp).append(suffix) }
}
/** A class that represents a type tree. */
private signature class TypeTreeSig {
Type resolveTypeAt(TypePath path);
/**
* A class that has a type tree associated with it.
*
* The type tree is represented by the `getTypeAt` predicate, which for every
* path into the tree gives the type at that path.
*/
signature class HasTypeTreeSig {
/** Gets the type at `path` in the type tree. */
Type getTypeAt(TypePath path);
/** Gets a textual representation of this type abstraction. */
/** Gets a textual representation of this type. */
string toString();
/** Gets the location of this type abstraction. */
/** Gets the location of this type. */
Location getLocation();
}
@@ -309,8 +315,8 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
* Holds if
* - `abs` is a type abstraction that introduces type variables that are
* free in `condition` and `constraint`,
* - and for every instantiation of the type parameters the resulting
* `condition` satisifies the constraint given by `constraint`.
* - and for every instantiation of the type parameters from `abs` the
* resulting `condition` satisifies the constraint given by `constraint`.
*
* Example in C#:
* ```csharp
@@ -322,12 +328,12 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*
* Example in Rust:
* ```rust
* impl<A> Trait<i64, B> for Type<String, A> { }
* impl<A> Trait<i64, A> for Type<String, A> { }
* // ^^^ `abs` ^^^^^^^^^^^^^^^ `condition`
* // ^^^^^^^^^^^^^ `constraint`
* ```
*
* To see how `abs` change the meaning of the type parameters that occur in
* To see how `abs` changes the meaning of the type parameters that occur in
* `condition`, consider the following examples in Rust:
* ```rust
* impl<T> Trait for T { }
@@ -362,10 +368,11 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
result = tm.resolveTypeAt(TypePath::nil())
}
signature module IsInstantiationOfInputSig<TypeTreeSig App> {
/** Provides the input to `IsInstantiationOf`. */
signature module IsInstantiationOfInputSig<HasTypeTreeSig App> {
/**
* Holds if `abs` is a type abstraction, `tm` occurs under `abs`, and
* `app` is potentially an application/instantiation of `abs`.
* Holds if `abs` is a type abstraction, `tm` occurs in the scope of
* `abs`, and `app` is potentially an application/instantiation of `abs`.
*
* For example:
* ```rust
@@ -378,8 +385,8 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
* foo.bar();
* // ^^^ `app`
* ```
* Here `abs` introduces the type parameter `A` and `tm` occurs under
* `abs` (i.e., `A` is bound in `tm` by `abs`). On the last line,
* Here `abs` introduces the type parameter `A` and `tm` occurs in the
* scope of `abs` (i.e., `A` is bound in `tm` by `abs`). On the last line,
* accessing the `bar` method of `foo` potentially instantiates the `impl`
* block with a type argument for `A`.
*/
@@ -397,7 +404,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
* Provides functionality for determining if a type is a possible
* instantiation of a type mention containing type parameters.
*/
module IsInstantiationOf<TypeTreeSig App, IsInstantiationOfInputSig<App> Input> {
module IsInstantiationOf<HasTypeTreeSig App, IsInstantiationOfInputSig<App> Input> {
private import Input
/** Gets the `i`th path in `tm` per some arbitrary order. */
@@ -422,7 +429,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
) {
exists(Type t |
tm.resolveTypeAt(path) = t and
if t = abs.getATypeParameter() then any() else app.resolveTypeAt(path) = t
if t = abs.getATypeParameter() then any() else app.getTypeAt(path) = t
)
}
@@ -436,6 +443,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
if i = 0 then any() else satisfiesConcreteTypesFromIndex(app, abs, tm, i - 1)
}
/** Holds if all the concrete types in `tm` also occur in `app`. */
pragma[nomagic]
private predicate satisfiesConcreteTypes(App app, TypeAbstraction abs, TypeMention tm) {
satisfiesConcreteTypesFromIndex(app, abs, tm, max(int i | exists(getNthPath(tm, i))))
@@ -463,18 +471,22 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
private predicate typeParametersEqualFromIndex(
App app, TypeAbstraction abs, TypeMention tm, TypeParameter tp, Type t, int i
) {
potentialInstantiationOf(app, abs, tm) and
satisfiesConcreteTypes(app, abs, tm) and
exists(TypePath path |
path = getNthTypeParameterPath(tm, tp, i) and
t = app.resolveTypeAt(path) and
if i = 0 then any() else typeParametersEqualFromIndex(app, abs, tm, tp, t, i - 1)
t = app.getTypeAt(path) and
if i = 0
then
// no need to compute this predicate if there is only one path
exists(getNthTypeParameterPath(tm, tp, 1))
else typeParametersEqualFromIndex(app, abs, tm, tp, t, i - 1)
)
}
private predicate typeParametersEqual(
App app, TypeAbstraction abs, TypeMention tm, TypeParameter tp
) {
potentialInstantiationOf(app, abs, tm) and
satisfiesConcreteTypes(app, abs, tm) and
tp = getNthTypeParameter(abs, _) and
(
not exists(getNthTypeParameterPath(tm, tp, _))
@@ -487,7 +499,6 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
)
}
/** Holds if all the concrete types in `tm` also occur in `app`. */
private predicate typeParametersHaveEqualInstantiationFromIndex(
App app, TypeAbstraction abs, TypeMention tm, int i
) {
@@ -499,12 +510,20 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
)
}
/** All the places where the same type parameter occurs in `tm` are equal in `app. */
pragma[inline]
/**
* Holds if all the places where the same type parameter occurs in `tm`
* are equal in `app`.
*
* TODO: As of now this only checks equality at the root of the types
* instantiated for type parameters. So, for instance, `Pair<Vec<i64>, Vec<bool>>`
* is mistakenly considered an instantiation of `Pair<A, A>`.
*/
pragma[nomagic]
private predicate typeParametersHaveEqualInstantiation(
App app, TypeAbstraction abs, TypeMention tm
) {
potentialInstantiationOf(app, abs, tm) and
// We only need to check equality if the concrete types are satisfied.
satisfiesConcreteTypes(app, abs, tm) and
(
not exists(getNthTypeParameter(abs, _))
or
@@ -527,7 +546,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
* - `Pair<int, string>` is _not_ an instantiation of `Pair<string, string>`
*/
predicate isInstantiationOf(App app, TypeAbstraction abs, TypeMention tm) {
satisfiesConcreteTypes(app, abs, tm) and
// `typeParametersHaveEqualInstantiation` suffices as it implies `satisfiesConcreteTypes`.
typeParametersHaveEqualInstantiation(app, abs, tm)
}
}
@@ -536,14 +555,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
private module BaseTypes {
/**
* Holds if, when `tm1` is considered an instantiation of `tm2`, then at
* the type parameter `tp` is has the type `t` at `path`.
* the type parameter `tp` it has the type `t` at `path`.
*
* For instance, if the type `Map<int, List<int>>` is considered an
* instantion of `Map<K, V>` then it has the type `int` at `K` and the
* type `List<int>` at `V`.
*/
bindingset[tm1, tm2]
predicate instantiatesWith(
private predicate instantiatesWith(
TypeMention tm1, TypeMention tm2, TypeParameter tp, TypePath path, Type t
) {
exists(TypePath prefix |
@@ -551,19 +570,27 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
)
}
module IsInstantiationOfInput implements IsInstantiationOfInputSig<TypeMention> {
final private class FinalTypeMention = TypeMention;
final private class TypeMentionTypeTree extends FinalTypeMention {
Type getTypeAt(TypePath path) { result = this.resolveTypeAt(path) }
}
private module IsInstantiationOfInput implements
IsInstantiationOfInputSig<TypeMentionTypeTree>
{
pragma[nomagic]
private predicate typeCondition(Type type, TypeAbstraction abs, TypeMention lhs) {
private predicate typeCondition(Type type, TypeAbstraction abs, TypeMentionTypeTree lhs) {
conditionSatisfiesConstraint(abs, lhs, _) and type = resolveTypeMentionRoot(lhs)
}
pragma[nomagic]
private predicate typeConstraint(Type type, TypeMention rhs) {
private predicate typeConstraint(Type type, TypeMentionTypeTree rhs) {
conditionSatisfiesConstraint(_, _, rhs) and type = resolveTypeMentionRoot(rhs)
}
predicate potentialInstantiationOf(
TypeMention condition, TypeAbstraction abs, TypeMention constraint
TypeMentionTypeTree condition, TypeAbstraction abs, TypeMention constraint
) {
exists(Type type |
typeConstraint(type, condition) and typeCondition(type, abs, constraint)
@@ -571,7 +598,10 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
}
// The type mention `condition` satisfies `constraint` with the type `t` at the path `path`.
/**
* The type mention `condition` satisfies `constraint` with the type `t`
* at the path `path`.
*/
predicate conditionSatisfiesConstraintTypeAt(
TypeAbstraction abs, TypeMention condition, TypeMention constraint, TypePath path, Type t
) {
@@ -584,18 +614,17 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
conditionSatisfiesConstraint(abs, condition, midSup) and
// NOTE: `midAbs` describe the free type variables in `midSub`, hence
// we use that for instantiation check.
IsInstantiationOf<TypeMention, IsInstantiationOfInput>::isInstantiationOf(midSup, midAbs,
midSub) and
(
conditionSatisfiesConstraintTypeAt(midAbs, midSub, constraint, path, t) and
not t = abs.getATypeParameter()
or
exists(TypePath prefix, TypePath suffix, TypeParameter tp |
tp = abs.getATypeParameter() and
conditionSatisfiesConstraintTypeAt(midAbs, midSub, constraint, prefix, tp) and
instantiatesWith(midSup, midSub, tp, suffix, t) and
path = prefix.append(suffix)
)
IsInstantiationOf<TypeMentionTypeTree, IsInstantiationOfInput>::isInstantiationOf(midSup,
midAbs, midSub)
|
conditionSatisfiesConstraintTypeAt(midAbs, midSub, constraint, path, t) and
not t = midAbs.getATypeParameter()
or
exists(TypePath prefix, TypePath suffix, TypeParameter tp |
tp = midAbs.getATypeParameter() and
conditionSatisfiesConstraintTypeAt(midAbs, midSub, constraint, prefix, tp) and
instantiatesWith(midSup, midSub, tp, suffix, t) and
path = prefix.append(suffix)
)
)
}
@@ -954,35 +983,37 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
private module AccessConstraint {
private newtype TTRelevantAccess =
TRelevantAccess(Access a, AccessPosition apos, TypePath path, Type constraint) {
exists(DeclarationPosition dpos |
accessDeclarationPositionMatch(apos, dpos) and
typeParameterConstraintHasTypeParameter(a.getTarget(), dpos, path, _, constraint, _, _)
)
predicate relevantAccessConstraint(
Access a, AccessPosition apos, TypePath path, Type constraint
) {
exists(DeclarationPosition dpos |
accessDeclarationPositionMatch(apos, dpos) and
typeParameterConstraintHasTypeParameter(a.getTarget(), dpos, path, _, constraint, _, _)
)
}
private newtype TRelevantAccess =
MkRelevantAccess(Access a, AccessPosition apos, TypePath path) {
relevantAccessConstraint(a, apos, path, _)
}
/**
* If the access `a` for `apos` and `path` has the inferred root type
* `type` and type inference requires it to satisfy the constraint
* `constraint`.
* If the access `a` for `apos` and `path` has an inferred type which
* type inference requires to satisfy some constraint.
*/
private class RelevantAccess extends TTRelevantAccess {
private class RelevantAccess extends MkRelevantAccess {
Access a;
AccessPosition apos;
TypePath path;
Type constraint0;
RelevantAccess() { this = TRelevantAccess(a, apos, path, constraint0) }
RelevantAccess() { this = MkRelevantAccess(a, apos, path) }
Type resolveTypeAt(TypePath suffix) {
a.getInferredType(apos, path.append(suffix)) = result
}
Type getTypeAt(TypePath suffix) { a.getInferredType(apos, path.append(suffix)) = result }
/** Holds if this relevant access has the type `type` and should satisfy `constraint`. */
predicate hasTypeConstraint(Type type, Type constraint) {
type = a.getInferredType(apos, path) and
constraint = constraint0
relevantAccessConstraint(a, apos, path, constraint)
}
string toString() {
@@ -1013,9 +1044,10 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
* Holds if `at` satisfies `constraint` through `abs`, `sub`, and `constraintMention`.
*/
private predicate hasConstraintMention(
RelevantAccess at, TypeAbstraction abs, TypeMention sub, TypeMention constraintMention
RelevantAccess at, TypeAbstraction abs, TypeMention sub, Type constraint,
TypeMention constraintMention
) {
exists(Type type, Type constraint | at.hasTypeConstraint(type, constraint) |
exists(Type type | at.hasTypeConstraint(type, constraint) |
not exists(countConstraintImplementations(type, constraint)) and
conditionSatisfiesConstraintTypeAt(abs, sub, constraintMention, _, _) and
resolveTypeMentionRoot(sub) = abs.getATypeParameter() and
@@ -1046,18 +1078,17 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
RelevantAccess at, TypeAbstraction abs, TypeMention sub, Type t0, TypePath prefix0,
TypeMention constraintMention
|
at = TRelevantAccess(a, apos, prefix, constraint) and
hasConstraintMention(at, abs, sub, constraintMention) and
conditionSatisfiesConstraintTypeAt(abs, sub, constraintMention, prefix0, t0) and
(
not t0 = abs.getATypeParameter() and t = t0 and path = prefix0
or
t0 = abs.getATypeParameter() and
exists(TypePath path3, TypePath suffix |
sub.resolveTypeAt(path3) = t0 and
at.resolveTypeAt(path3.append(suffix)) = t and
path = prefix0.append(suffix)
)
at = MkRelevantAccess(a, apos, prefix) and
hasConstraintMention(at, abs, sub, constraint, constraintMention) and
conditionSatisfiesConstraintTypeAt(abs, sub, constraintMention, prefix0, t0)
|
not t0 = abs.getATypeParameter() and t = t0 and path = prefix0
or
t0 = abs.getATypeParameter() and
exists(TypePath path3, TypePath suffix |
sub.resolveTypeAt(path3) = t0 and
at.getTypeAt(path3.append(suffix)) = t and
path = prefix0.append(suffix)
)
)
}