Skip to content

Commit f130198

Browse files
committed
Rust: Implement basic support for blanket implementations
1 parent 854639d commit f130198

File tree

7 files changed

+202
-10
lines changed

7 files changed

+202
-10
lines changed

rust/ql/lib/codeql/rust/internal/PathResolution.qll

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -933,15 +933,36 @@ class TypeParamItemNode extends TypeItemNode instanceof TypeParam {
933933
}
934934

935935
pragma[nomagic]
936-
Path getABoundPath() {
937-
exists(TypeBoundList tbl | result = tbl.getABound().getTypeRepr().(PathTypeRepr).getPath() |
938-
tbl = super.getTypeBoundList()
936+
TypeBound getBound(int index) {
937+
result = super.getTypeBoundList().getBound(index)
938+
or
939+
exists(int offset |
940+
offset = super.getTypeBoundList().getNumberOfBounds()
939941
or
940-
tbl = this.getAWherePred().getTypeBoundList()
942+
not super.hasTypeBoundList() and
943+
offset = 0
944+
|
945+
result = this.getAWherePred().getTypeBoundList().getBound(index - offset)
941946
)
942947
}
943948

944949
pragma[nomagic]
950+
Path getBoundPath(int index) {
951+
result = this.getBound(index).getTypeRepr().(PathTypeRepr).getPath()
952+
}
953+
954+
Path getABoundPath() { result = this.getBoundPath(_) }
955+
956+
pragma[nomagic]
957+
ItemNode resolveBound(int index) {
958+
result =
959+
rank[index + 1](int i, ItemNode item |
960+
item = resolvePath(this.getBoundPath(i))
961+
|
962+
item order by i
963+
)
964+
}
965+
945966
ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) }
946967

947968
/**

rust/ql/lib/codeql/rust/internal/TypeInference.qll

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,10 @@ private predicate methodCandidateTrait(Type type, Trait trait, string name, int
16431643
methodCandidate(type, name, arity, impl)
16441644
}
16451645

1646+
/**
1647+
* Holds if `mc` has `rootType` as the root type of the reciever and the target
1648+
* method is named `name` and has arity `arity`
1649+
*/
16461650
pragma[nomagic]
16471651
private predicate isMethodCall(MethodCall mc, Type rootType, string name, int arity) {
16481652
rootType = mc.getTypeAt(TypePath::nil()) and
@@ -1841,6 +1845,142 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) {
18411845
else any()
18421846
}
18431847

1848+
private module BlanketImplementation {
1849+
/**
1850+
* Holds if `impl` is a blanket implementation, that is, an implementation of a
1851+
* trait for a type parameter.
1852+
*/
1853+
private TypeParamItemNode getBlanketImplementationTypeParam(Impl impl) {
1854+
result = impl.(ImplItemNode).resolveSelfTy() and
1855+
result = impl.getGenericParamList().getAGenericParam() and
1856+
not exists(impl.getAttributeMacroExpansion())
1857+
}
1858+
1859+
predicate isBlanketImplementation(Impl impl) { exists(getBlanketImplementationTypeParam(impl)) }
1860+
1861+
private Impl getPotentialDuplicated(string fileName, string traitName, int arity, string tpName) {
1862+
tpName = getBlanketImplementationTypeParam(result).getName() and
1863+
fileName = result.getLocation().getFile().getBaseName() and
1864+
traitName = result.(ImplItemNode).resolveTraitTy().getName() and
1865+
arity = result.(ImplItemNode).resolveTraitTy().(Trait).getNumberOfGenericParams()
1866+
}
1867+
1868+
/**
1869+
* Holds if `impl1` and `impl2` are duplicates and `impl2` is more "canonical"
1870+
* than `impl1`.
1871+
*/
1872+
predicate duplicatedImpl(Impl impl1, Impl impl2) {
1873+
exists(string fileName, string traitName, int arity, string tpName |
1874+
impl1 = getPotentialDuplicated(fileName, traitName, arity, tpName) and
1875+
impl2 = getPotentialDuplicated(fileName, traitName, arity, tpName) and
1876+
impl1.getLocation().getFile().getAbsolutePath() <
1877+
impl2.getLocation().getFile().getAbsolutePath()
1878+
)
1879+
}
1880+
1881+
predicate hasNoDuplicates(Impl impl) {
1882+
not duplicatedImpl(impl, _) and isBlanketImplementation(impl)
1883+
}
1884+
1885+
/**
1886+
* We currently consider blanket implementations to be in scope "globally",
1887+
* even though they actually need to be imported to be used. One downside of
1888+
* this is that the libraries included in the database can often occur several
1889+
* times for different library versions. This causes the same blanket
1890+
* implementations to exist multiple times, and these add no useful
1891+
* information.
1892+
*
1893+
* We detect these duplicates based on some files heuristic (same trait name,
1894+
* file name, etc.). For these duplicates we select the one with the greatest
1895+
* file name (which usually is also the one with the greatest library version
1896+
* in the path)
1897+
*/
1898+
Impl getCanonicalImpl(Impl impl) {
1899+
result =
1900+
max(Impl impl0, Location l |
1901+
duplicatedImpl(impl, impl0) and l = impl0.getLocation()
1902+
|
1903+
impl0 order by l.getFile().getAbsolutePath(), l.getStartLine()
1904+
)
1905+
or
1906+
hasNoDuplicates(impl) and result = impl
1907+
}
1908+
1909+
predicate isCanonicalBlanketImplementation(Impl impl) { impl = getCanonicalImpl(impl) }
1910+
1911+
/**
1912+
* Holds if `impl` is a blanket implementation for a type parameter and the type
1913+
* parameter must implement `trait`.
1914+
*/
1915+
private predicate blanketImplementationTraitBound(Impl impl, Trait t) {
1916+
t =
1917+
min(Trait trait, int i |
1918+
trait = getBlanketImplementationTypeParam(impl).resolveBound(i) and
1919+
// Exclude traits that are "trivial" in the sense that they are known to
1920+
// not narrow things down very much.
1921+
not trait.getName().getText() =
1922+
[
1923+
"Sized", "Clone", "Fn", "FnOnce", "FnMut",
1924+
// The auto traits
1925+
"Send", "Sync", "Unpin", "UnwindSafe", "RefUnwindSafe"
1926+
]
1927+
|
1928+
trait order by i
1929+
)
1930+
}
1931+
1932+
private predicate blanketImplementationMethod(
1933+
Impl impl, Trait trait, string name, int arity, Function f
1934+
) {
1935+
isCanonicalBlanketImplementation(impl) and
1936+
blanketImplementationTraitBound(impl, trait) and
1937+
f.getParamList().hasSelfParam() and
1938+
arity = f.getParamList().getNumberOfParams() and
1939+
(
1940+
f = impl.(ImplItemNode).getAssocItem(name)
1941+
or
1942+
// If the the trait has a method with a default implementation, then that
1943+
// target is interesting as well.
1944+
not exists(impl.(ImplItemNode).getAssocItem(name)) and
1945+
f = impl.(ImplItemNode).resolveTraitTy().getAssocItem(name)
1946+
) and
1947+
// If the method is already available through one of the trait bounds on the
1948+
// type parameter (because they share a common trait ancestor) then ignore
1949+
// it.
1950+
not getBlanketImplementationTypeParam(impl).resolveABound().(TraitItemNode).getASuccessor(name) =
1951+
f
1952+
}
1953+
1954+
predicate methodCallMatchesBlanketImpl(MethodCall mc, Type t, Impl impl, Trait trait, Function f) {
1955+
// Only check method calls where we have ruled out inherent method targets.
1956+
// Ideally we would also check if non-blanket method targets have been ruled
1957+
// out.
1958+
methodCallHasNoInherentTarget(mc) and
1959+
exists(string name, int arity |
1960+
isMethodCall(mc, t, name, arity) and
1961+
blanketImplementationMethod(impl, trait, name, arity, f)
1962+
)
1963+
}
1964+
1965+
module SatisfiesConstraintInput implements SatisfiesConstraintInputSig<MethodCall> {
1966+
pragma[nomagic]
1967+
predicate relevantConstraint(MethodCall mc, Type constraint) {
1968+
methodCallMatchesBlanketImpl(mc, _, _, constraint.(TraitType).getTrait(), _)
1969+
}
1970+
1971+
predicate useUniversalConditions() { none() }
1972+
}
1973+
1974+
predicate hasBlanketImpl(MethodCall mc, Type t, Impl impl, Trait trait, Function f) {
1975+
SatisfiesConstraint<MethodCall, SatisfiesConstraintInput>::satisfiesConstraintType(mc,
1976+
TTrait(trait), _, _) and
1977+
methodCallMatchesBlanketImpl(mc, t, impl, trait, f)
1978+
}
1979+
1980+
pragma[nomagic]
1981+
Function getMethodFromBlanketImpl(MethodCall mc) { hasBlanketImpl(mc, _, _, _, result) }
1982+
}
1983+
18441984
/** Gets a method from an `impl` block that matches the method call `mc`. */
18451985
pragma[nomagic]
18461986
private Function getMethodFromImpl(MethodCall mc) {
@@ -1876,6 +2016,8 @@ private Function resolveMethodCallTarget(MethodCall mc) {
18762016
// The method comes from an `impl` block targeting the type of the receiver.
18772017
result = getMethodFromImpl(mc)
18782018
or
2019+
result = BlanketImplementation::getMethodFromBlanketImpl(mc)
2020+
or
18792021
// The type of the receiver is a type parameter and the method comes from a
18802022
// trait bound on the type parameter.
18812023
result = getTypeParameterMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName())

rust/ql/test/library-tests/type-inference/blanket_impl.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ mod basic_blanket_impl {
3232
pub fn test_basic_blanket() {
3333
let x = S1.clone1(); // $ target=S1::clone1
3434
println!("{x:?}");
35-
let y = S1.duplicate(); // $ MISSING: target=Clone1duplicate
35+
let y = S1.duplicate(); // $ target=Clone1duplicate
3636
println!("{y:?}");
3737
}
3838
}
@@ -108,7 +108,7 @@ mod extension_trait_blanket_impl {
108108

109109
fn test() {
110110
let my_try_flag = MyTryFlag { flag: true };
111-
let result = my_try_flag.try_read_flag_twice(); // $ MISSING: target=TryFlagExt::try_read_flag_twice
111+
let result = my_try_flag.try_read_flag_twice(); // $ target=TryFlagExt::try_read_flag_twice
112112

113113
let my_flag = MyFlag { flag: true };
114114
// Here `TryFlagExt::try_read_flag_twice` is since there is a blanket

rust/ql/test/library-tests/type-inference/dyn_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn test_assoc_type(obj: &dyn AssocTrait<i64, AP = bool>) {
101101
pub fn test() {
102102
test_basic_dyn_trait(&MyStruct { value: 42 }); // $ target=test_basic_dyn_trait
103103
test_generic_dyn_trait(&GenStruct {
104-
value: "".to_string(),
104+
value: "".to_string(), // $ target=to_string
105105
}); // $ target=test_generic_dyn_trait
106106
test_poly_dyn_trait(); // $ target=test_poly_dyn_trait
107107
test_assoc_type(&GenStruct { value: 100 }); // $ target=test_assoc_type

rust/ql/test/library-tests/type-inference/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ mod method_non_parametric_trait_impl {
316316

317317
fn type_bound_type_parameter_impl<TP: MyTrait<S1>>(thing: TP) -> S1 {
318318
// The trait bound on `TP` makes the implementation of `ConvertTo` valid
319-
thing.convert_to() // $ MISSING: target=T::convert_to
319+
thing.convert_to() // $ target=T::convert_to
320320
}
321321

322322
pub fn f() {
@@ -388,7 +388,7 @@ mod method_non_parametric_trait_impl {
388388
let x = get_snd_fst(c); // $ type=x:S1 target=get_snd_fst
389389

390390
let thing = MyThing { a: S1 };
391-
let i = thing.convert_to(); // $ MISSING: type=i:S1 target=T::convert_to
391+
let i = thing.convert_to(); // $ type=i:S1 target=T::convert_to
392392
let j = convert_to(thing); // $ type=j:S1 target=convert_to
393393
}
394394
}
@@ -1292,7 +1292,7 @@ mod method_call_type_conversion {
12921292
let t = x7.m1(); // $ target=m1 type=t:& type=t:&T.S2
12931293
println!("{:?}", x7);
12941294

1295-
let x9: String = "Hello".to_string(); // $ type=x9:String
1295+
let x9: String = "Hello".to_string(); // $ type=x9:String target=to_string
12961296

12971297
// Implicit `String` -> `str` conversion happens via the `Deref` trait:
12981298
// https://doc.rust-lang.org/std/string/struct.String.html#deref.

rust/ql/test/library-tests/type-inference/type-inference.expected

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ inferType
2323
| blanket_impl.rs:34:18:34:24 | FormatArgsExpr | | {EXTERNAL LOCATION} | Arguments |
2424
| blanket_impl.rs:34:18:34:24 | MacroExpr | | {EXTERNAL LOCATION} | Arguments |
2525
| blanket_impl.rs:34:20:34:20 | x | | blanket_impl.rs:4:5:5:14 | S1 |
26+
| blanket_impl.rs:35:13:35:13 | y | | blanket_impl.rs:4:5:5:14 | S1 |
2627
| blanket_impl.rs:35:17:35:18 | S1 | | blanket_impl.rs:4:5:5:14 | S1 |
28+
| blanket_impl.rs:35:17:35:30 | S1.duplicate() | | blanket_impl.rs:4:5:5:14 | S1 |
2729
| blanket_impl.rs:36:18:36:24 | "{y:?}\\n" | | file://:0:0:0:0 | & |
2830
| blanket_impl.rs:36:18:36:24 | "{y:?}\\n" | &T | {EXTERNAL LOCATION} | str |
2931
| blanket_impl.rs:36:18:36:24 | FormatArgsExpr | | {EXTERNAL LOCATION} | Arguments |
3032
| blanket_impl.rs:36:18:36:24 | MacroExpr | | {EXTERNAL LOCATION} | Arguments |
33+
| blanket_impl.rs:36:20:36:20 | y | | blanket_impl.rs:4:5:5:14 | S1 |
3134
| blanket_impl.rs:46:22:46:26 | SelfParam | | file://:0:0:0:0 | & |
3235
| blanket_impl.rs:46:22:46:26 | SelfParam | &T | blanket_impl.rs:45:5:47:5 | Self [trait Flag] |
3336
| blanket_impl.rs:50:26:50:30 | SelfParam | | file://:0:0:0:0 | & |
@@ -78,7 +81,11 @@ inferType
7881
| blanket_impl.rs:110:13:110:23 | my_try_flag | | blanket_impl.rs:76:5:78:5 | MyTryFlag |
7982
| blanket_impl.rs:110:27:110:50 | MyTryFlag {...} | | blanket_impl.rs:76:5:78:5 | MyTryFlag |
8083
| blanket_impl.rs:110:45:110:48 | true | | {EXTERNAL LOCATION} | bool |
84+
| blanket_impl.rs:111:13:111:18 | result | | {EXTERNAL LOCATION} | Option |
85+
| blanket_impl.rs:111:13:111:18 | result | T | {EXTERNAL LOCATION} | bool |
8186
| blanket_impl.rs:111:22:111:32 | my_try_flag | | blanket_impl.rs:76:5:78:5 | MyTryFlag |
87+
| blanket_impl.rs:111:22:111:54 | my_try_flag.try_read_flag_twice() | | {EXTERNAL LOCATION} | Option |
88+
| blanket_impl.rs:111:22:111:54 | my_try_flag.try_read_flag_twice() | T | {EXTERNAL LOCATION} | bool |
8289
| blanket_impl.rs:113:13:113:19 | my_flag | | blanket_impl.rs:87:5:89:5 | MyFlag |
8390
| blanket_impl.rs:113:23:113:43 | MyFlag {...} | | blanket_impl.rs:87:5:89:5 | MyFlag |
8491
| blanket_impl.rs:113:38:113:41 | true | | {EXTERNAL LOCATION} | bool |
@@ -649,12 +656,15 @@ inferType
649656
| dyn_type.rs:103:28:105:5 | &... | | file://:0:0:0:0 | & |
650657
| dyn_type.rs:103:28:105:5 | &... | &T | dyn_type.rs:10:1:13:1 | dyn GenericGet |
651658
| dyn_type.rs:103:28:105:5 | &... | &T | dyn_type.rs:33:1:36:1 | GenStruct |
659+
| dyn_type.rs:103:28:105:5 | &... | &T.A | {EXTERNAL LOCATION} | String |
652660
| dyn_type.rs:103:28:105:5 | &... | &T.dyn(A) | {EXTERNAL LOCATION} | String |
653661
| dyn_type.rs:103:29:105:5 | GenStruct {...} | | dyn_type.rs:10:1:13:1 | dyn GenericGet |
654662
| dyn_type.rs:103:29:105:5 | GenStruct {...} | | dyn_type.rs:33:1:36:1 | GenStruct |
663+
| dyn_type.rs:103:29:105:5 | GenStruct {...} | A | {EXTERNAL LOCATION} | String |
655664
| dyn_type.rs:103:29:105:5 | GenStruct {...} | dyn(A) | {EXTERNAL LOCATION} | String |
656665
| dyn_type.rs:104:16:104:17 | "" | | file://:0:0:0:0 | & |
657666
| dyn_type.rs:104:16:104:17 | "" | &T | {EXTERNAL LOCATION} | str |
667+
| dyn_type.rs:104:16:104:29 | "".to_string() | | {EXTERNAL LOCATION} | String |
658668
| dyn_type.rs:107:21:107:45 | &... | | file://:0:0:0:0 | & |
659669
| dyn_type.rs:107:21:107:45 | &... | &T | dyn_type.rs:15:1:19:1 | dyn AssocTrait |
660670
| dyn_type.rs:107:21:107:45 | &... | &T | dyn_type.rs:33:1:36:1 | GenStruct |
@@ -1267,8 +1277,10 @@ inferType
12671277
| main.rs:390:21:390:37 | MyThing {...} | | main.rs:175:5:178:5 | MyThing |
12681278
| main.rs:390:21:390:37 | MyThing {...} | A | main.rs:186:5:187:14 | S1 |
12691279
| main.rs:390:34:390:35 | S1 | | main.rs:186:5:187:14 | S1 |
1280+
| main.rs:391:13:391:13 | i | | main.rs:186:5:187:14 | S1 |
12701281
| main.rs:391:17:391:21 | thing | | main.rs:175:5:178:5 | MyThing |
12711282
| main.rs:391:17:391:21 | thing | A | main.rs:186:5:187:14 | S1 |
1283+
| main.rs:391:17:391:34 | thing.convert_to() | | main.rs:186:5:187:14 | S1 |
12721284
| main.rs:392:13:392:13 | j | | main.rs:186:5:187:14 | S1 |
12731285
| main.rs:392:17:392:33 | convert_to(...) | | main.rs:186:5:187:14 | S1 |
12741286
| main.rs:392:28:392:32 | thing | | main.rs:175:5:178:5 | MyThing |
@@ -3168,14 +3180,22 @@ inferType
31683180
| main.rs:1622:13:1625:13 | Vec2 {...} | | main.rs:1502:5:1507:5 | Vec2 |
31693181
| main.rs:1623:20:1623:23 | self | | main.rs:1502:5:1507:5 | Vec2 |
31703182
| main.rs:1623:20:1623:25 | self.x | | {EXTERNAL LOCATION} | i64 |
3183+
| main.rs:1623:20:1623:33 | ... \| ... | | {EXTERNAL LOCATION} | NonZero |
31713184
| main.rs:1623:20:1623:33 | ... \| ... | | {EXTERNAL LOCATION} | i64 |
3185+
| main.rs:1623:20:1623:33 | ... \| ... | T | {EXTERNAL LOCATION} | i64 |
31723186
| main.rs:1623:29:1623:31 | rhs | | main.rs:1502:5:1507:5 | Vec2 |
3187+
| main.rs:1623:29:1623:33 | rhs.x | | {EXTERNAL LOCATION} | NonZero |
31733188
| main.rs:1623:29:1623:33 | rhs.x | | {EXTERNAL LOCATION} | i64 |
3189+
| main.rs:1623:29:1623:33 | rhs.x | T | {EXTERNAL LOCATION} | i64 |
31743190
| main.rs:1624:20:1624:23 | self | | main.rs:1502:5:1507:5 | Vec2 |
31753191
| main.rs:1624:20:1624:25 | self.y | | {EXTERNAL LOCATION} | i64 |
3192+
| main.rs:1624:20:1624:33 | ... \| ... | | {EXTERNAL LOCATION} | NonZero |
31763193
| main.rs:1624:20:1624:33 | ... \| ... | | {EXTERNAL LOCATION} | i64 |
3194+
| main.rs:1624:20:1624:33 | ... \| ... | T | {EXTERNAL LOCATION} | i64 |
31773195
| main.rs:1624:29:1624:31 | rhs | | main.rs:1502:5:1507:5 | Vec2 |
3196+
| main.rs:1624:29:1624:33 | rhs.y | | {EXTERNAL LOCATION} | NonZero |
31783197
| main.rs:1624:29:1624:33 | rhs.y | | {EXTERNAL LOCATION} | i64 |
3198+
| main.rs:1624:29:1624:33 | rhs.y | T | {EXTERNAL LOCATION} | i64 |
31793199
| main.rs:1630:25:1630:33 | SelfParam | | file://:0:0:0:0 | & |
31803200
| main.rs:1630:25:1630:33 | SelfParam | &T | main.rs:1502:5:1507:5 | Vec2 |
31813201
| main.rs:1630:36:1630:38 | rhs | | main.rs:1502:5:1507:5 | Vec2 |
@@ -3513,10 +3533,16 @@ inferType
35133533
| main.rs:1773:26:1773:30 | 33i64 | | {EXTERNAL LOCATION} | i64 |
35143534
| main.rs:1773:26:1773:38 | ... & ... | | {EXTERNAL LOCATION} | i64 |
35153535
| main.rs:1773:34:1773:38 | 34i64 | | {EXTERNAL LOCATION} | i64 |
3536+
| main.rs:1774:13:1774:21 | i64_bitor | | {EXTERNAL LOCATION} | NonZero |
35163537
| main.rs:1774:13:1774:21 | i64_bitor | | {EXTERNAL LOCATION} | i64 |
3538+
| main.rs:1774:13:1774:21 | i64_bitor | T | {EXTERNAL LOCATION} | i64 |
35173539
| main.rs:1774:25:1774:29 | 35i64 | | {EXTERNAL LOCATION} | i64 |
3540+
| main.rs:1774:25:1774:37 | ... \| ... | | {EXTERNAL LOCATION} | NonZero |
35183541
| main.rs:1774:25:1774:37 | ... \| ... | | {EXTERNAL LOCATION} | i64 |
3542+
| main.rs:1774:25:1774:37 | ... \| ... | T | {EXTERNAL LOCATION} | i64 |
3543+
| main.rs:1774:33:1774:37 | 36i64 | | {EXTERNAL LOCATION} | NonZero |
35193544
| main.rs:1774:33:1774:37 | 36i64 | | {EXTERNAL LOCATION} | i64 |
3545+
| main.rs:1774:33:1774:37 | 36i64 | T | {EXTERNAL LOCATION} | i64 |
35203546
| main.rs:1775:13:1775:22 | i64_bitxor | | {EXTERNAL LOCATION} | i64 |
35213547
| main.rs:1775:26:1775:30 | 37i64 | | {EXTERNAL LOCATION} | i64 |
35223548
| main.rs:1775:26:1775:38 | ... ^ ... | | {EXTERNAL LOCATION} | i64 |

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,8 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
903903
signature module SatisfiesConstraintInputSig<HasTypeTreeSig HasTypeTree> {
904904
/** Holds if it is relevant to know if `term` satisfies `constraint`. */
905905
predicate relevantConstraint(HasTypeTree term, Type constraint);
906+
907+
default predicate useUniversalConditions() { any() }
906908
}
907909

908910
module SatisfiesConstraint<
@@ -944,6 +946,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
944946
not exists(countConstraintImplementations(type, constraint)) and
945947
conditionSatisfiesConstraintTypeAt(abs, sub, constraintMention, _, _) and
946948
resolveTypeMentionRoot(sub) = abs.getATypeParameter() and
949+
useUniversalConditions() and
947950
constraint = resolveTypeMentionRoot(constraintMention)
948951
or
949952
countConstraintImplementations(type, constraint) > 0 and

0 commit comments

Comments
 (0)