Shared: Generalize SatisfiesConstraint module

This commit is contained in:
Tom Hvitved
2026-03-17 15:41:41 +01:00
parent 082dc61620
commit 06f0c1189f
4 changed files with 129 additions and 94 deletions

View File

@@ -103,7 +103,7 @@ module SatisfiesBlanketConstraint<
}
private module SatisfiesBlanketConstraintInput implements
SatisfiesConstraintInputSig<ArgumentTypeAndBlanketOffset>
SatisfiesTypeInputSig<ArgumentTypeAndBlanketOffset>
{
pragma[nomagic]
additional predicate relevantConstraint(
@@ -123,7 +123,7 @@ module SatisfiesBlanketConstraint<
}
private module SatisfiesBlanketConstraint =
SatisfiesConstraint<ArgumentTypeAndBlanketOffset, SatisfiesBlanketConstraintInput>;
SatisfiesType<ArgumentTypeAndBlanketOffset, SatisfiesBlanketConstraintInput>;
/**
* Holds if the argument type `at` satisfies the first non-trivial blanket

View File

@@ -2362,8 +2362,7 @@ private module AssocFunctionResolution {
Location getLocation() { result = afc.getLocation() }
}
private module CallSatisfiesDerefConstraintInput implements
SatisfiesConstraintInputSig<CallDerefCand>
private module CallSatisfiesDerefConstraintInput implements SatisfiesTypeInputSig<CallDerefCand>
{
pragma[nomagic]
predicate relevantConstraint(CallDerefCand mc, Type constraint) {
@@ -2373,7 +2372,7 @@ private module AssocFunctionResolution {
}
private module CallSatisfiesDerefConstraint =
SatisfiesConstraint<CallDerefCand, CallSatisfiesDerefConstraintInput>;
SatisfiesType<CallDerefCand, CallSatisfiesDerefConstraintInput>;
pragma[nomagic]
private AssociatedTypeTypeParameter getDerefTargetTypeParameter() {
@@ -3466,7 +3465,7 @@ final private class AwaitTarget extends Expr {
Type getTypeAt(TypePath path) { result = inferType(this, path) }
}
private module AwaitSatisfiesConstraintInput implements SatisfiesConstraintInputSig<AwaitTarget> {
private module AwaitSatisfiesTypeInput implements SatisfiesTypeInputSig<AwaitTarget> {
pragma[nomagic]
predicate relevantConstraint(AwaitTarget term, Type constraint) {
exists(term) and
@@ -3474,13 +3473,12 @@ private module AwaitSatisfiesConstraintInput implements SatisfiesConstraintInput
}
}
private module AwaitSatisfiesConstraint =
SatisfiesConstraint<AwaitTarget, AwaitSatisfiesConstraintInput>;
private module AwaitSatisfiesType = SatisfiesType<AwaitTarget, AwaitSatisfiesTypeInput>;
pragma[nomagic]
private Type inferAwaitExprType(AstNode n, TypePath path) {
exists(TypePath exprPath |
AwaitSatisfiesConstraint::satisfiesConstraintType(n.(AwaitExpr).getExpr(), _, exprPath, result) and
AwaitSatisfiesType::satisfiesConstraintType(n.(AwaitExpr).getExpr(), _, exprPath, result) and
exprPath.isCons(getFutureOutputTypeParameter(), path)
)
}
@@ -3616,9 +3614,7 @@ final private class ForIterableExpr extends Expr {
Type getTypeAt(TypePath path) { result = inferType(this, path) }
}
private module ForIterableSatisfiesConstraintInput implements
SatisfiesConstraintInputSig<ForIterableExpr>
{
private module ForIterableSatisfiesTypeInput implements SatisfiesTypeInputSig<ForIterableExpr> {
predicate relevantConstraint(ForIterableExpr term, Type constraint) {
exists(term) and
exists(Trait t | t = constraint.(TraitType).getTrait() |
@@ -3639,15 +3635,15 @@ private AssociatedTypeTypeParameter getIntoIteratorItemTypeParameter() {
result = getAssociatedTypeTypeParameter(any(IntoIteratorTrait t).getItemType())
}
private module ForIterableSatisfiesConstraint =
SatisfiesConstraint<ForIterableExpr, ForIterableSatisfiesConstraintInput>;
private module ForIterableSatisfiesType =
SatisfiesType<ForIterableExpr, ForIterableSatisfiesTypeInput>;
pragma[nomagic]
private Type inferForLoopExprType(AstNode n, TypePath path) {
// type of iterable -> type of pattern (loop variable)
exists(ForExpr fe, TypePath exprPath, AssociatedTypeTypeParameter tp |
n = fe.getPat() and
ForIterableSatisfiesConstraint::satisfiesConstraintType(fe.getIterable(), _, exprPath, result) and
ForIterableSatisfiesType::satisfiesConstraintType(fe.getIterable(), _, exprPath, result) and
exprPath.isCons(tp, path)
|
tp = getIntoIteratorItemTypeParameter()
@@ -3673,8 +3669,7 @@ final private class InvokedClosureExpr extends Expr {
CallExpr getCall() { result = call }
}
private module InvokedClosureSatisfiesConstraintInput implements
SatisfiesConstraintInputSig<InvokedClosureExpr>
private module InvokedClosureSatisfiesTypeInput implements SatisfiesTypeInputSig<InvokedClosureExpr>
{
predicate relevantConstraint(InvokedClosureExpr term, Type constraint) {
exists(term) and
@@ -3682,12 +3677,12 @@ private module InvokedClosureSatisfiesConstraintInput implements
}
}
private module InvokedClosureSatisfiesConstraint =
SatisfiesConstraint<InvokedClosureExpr, InvokedClosureSatisfiesConstraintInput>;
private module InvokedClosureSatisfiesType =
SatisfiesType<InvokedClosureExpr, InvokedClosureSatisfiesTypeInput>;
/** Gets the type of `ce` when viewed as an implementation of `FnOnce`. */
private Type invokedClosureFnTypeAt(InvokedClosureExpr ce, TypePath path) {
InvokedClosureSatisfiesConstraint::satisfiesConstraintType(ce, _, path, result)
InvokedClosureSatisfiesType::satisfiesConstraintType(ce, _, path, result)
}
/**

View File

@@ -730,14 +730,14 @@ private predicate pathConcreteTypeAssocType(
)
}
private module PathSatisfiesConstraintInput implements SatisfiesConstraintInputSig<PreTypeMention> {
private module PathSatisfiesConstraintInput implements SatisfiesTypeInputSig<PreTypeMention> {
predicate relevantConstraint(PreTypeMention tm, Type constraint) {
pathConcreteTypeAssocType(_, tm, constraint.(TraitType).getTrait(), _, _)
}
}
private module PathSatisfiesConstraint =
SatisfiesConstraint<PreTypeMention, PathSatisfiesConstraintInput>;
SatisfiesType<PreTypeMention, PathSatisfiesConstraintInput>;
/**
* Gets the type of `path` at `typePath` when `path` accesses an associated type

View File

@@ -820,38 +820,43 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
private import BaseTypes
signature module SatisfiesConstraintInputSig<HasTypeTreeSig HasTypeTree> {
/** Provides the input to `SatisfiesConstraint`. */
signature module SatisfiesConstraintInputSig<HasTypeTreeSig Term, HasTypeTreeSig Constraint> {
/** Holds if it is relevant to know if `term` satisfies `constraint`. */
predicate relevantConstraint(HasTypeTree term, Type constraint);
predicate relevantConstraint(Term term, Constraint constraint);
}
module SatisfiesConstraint<
HasTypeTreeSig HasTypeTree, SatisfiesConstraintInputSig<HasTypeTree> Input>
HasTypeTreeSig Term, HasTypeTreeSig Constraint,
SatisfiesConstraintInputSig<Term, Constraint> Input>
{
private import Input
pragma[nomagic]
private Type getTypeAt(HasTypeTree term, TypePath path) {
private Type getTypeAt(Term term, TypePath path) {
relevantConstraint(term, _) and
result = term.getTypeAt(path)
}
/** Holds if the type tree has the type `type` and should satisfy `constraint`. */
pragma[nomagic]
private predicate hasTypeConstraint(HasTypeTree term, Type type, Type constraint) {
private predicate hasTypeConstraint(
Term term, Type type, Constraint constraint, Type constraintRoot
) {
type = getTypeAt(term, TypePath::nil()) and
relevantConstraint(term, constraint)
relevantConstraint(term, constraint) and
constraintRoot = constraint.getTypeAt(TypePath::nil())
}
private module IsInstantiationOfInput implements
IsInstantiationOfInputSig<HasTypeTree, TypeMention>
private module TermIsInstantiationOfConditionInput implements
IsInstantiationOfInputSig<Term, TypeMention>
{
predicate potentialInstantiationOf(HasTypeTree tt, TypeAbstraction abs, TypeMention cond) {
exists(Type constraint, Type type |
hasTypeConstraint(tt, type, constraint) and
rootTypesSatisfaction(type, constraint, abs, cond, _) and
predicate potentialInstantiationOf(Term term, TypeAbstraction abs, TypeMention cond) {
exists(Constraint constraint, Type type, Type constraintRoot |
hasTypeConstraint(term, type, constraint, constraintRoot) and
rootTypesSatisfaction(type, constraintRoot, abs, cond, _) and
// We only need to check instantiations where there are multiple candidates.
multipleConstraintImplementations(type, constraint)
multipleConstraintImplementations(type, constraintRoot)
)
}
@@ -860,18 +865,18 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
}
private module SatisfiesConstraintIsInstantiationOf =
IsInstantiationOf<HasTypeTree, TypeMention, IsInstantiationOfInput>;
private module TermIsInstantiationOfCondition =
IsInstantiationOf<Term, TypeMention, TermIsInstantiationOfConditionInput>;
/**
* Holds if `tt` satisfies `constraint` through `abs`, `sub`, and `constraintMention`.
* Holds if `term` satisfies `constraint` through `abs`, `sub`, and `constraintMention`.
*/
pragma[nomagic]
private predicate hasConstraintMention(
HasTypeTree tt, TypeAbstraction abs, TypeMention condition, Type constraint,
TypeMention constraintMention
Term term, TypeAbstraction abs, TypeMention condition, Constraint constraint,
Type constraintRoot, TypeMention constraintMention
) {
exists(Type type | hasTypeConstraint(tt, type, constraint) |
exists(Type type | hasTypeConstraint(term, type, constraint, constraintRoot) |
// TODO: Handle universal conditions properly, which means checking type parameter constraints
// Also remember to update logic in `hasNotConstraintMention`
//
@@ -880,35 +885,37 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
// getTypeMentionRoot(condition) = abs.getATypeParameter() and
// constraint = getTypeMentionRoot(constraintMention)
// or
countConstraintImplementations(type, constraint) > 0 and
rootTypesSatisfaction(type, constraint, abs, condition, constraintMention) and
countConstraintImplementations(type, constraintRoot) > 0 and
rootTypesSatisfaction(type, constraintRoot, abs, condition, constraintMention) and
// When there are multiple ways the type could implement the
// constraint we need to find the right implementation, which is the
// one where the type instantiates the precondition.
if multipleConstraintImplementations(type, constraint)
then SatisfiesConstraintIsInstantiationOf::isInstantiationOf(tt, abs, condition)
if multipleConstraintImplementations(type, constraintRoot)
then TermIsInstantiationOfCondition::isInstantiationOf(term, abs, condition)
else any()
)
}
pragma[nomagic]
private predicate isNotInstantiationOf(
HasTypeTree tt, TypeAbstraction abs, TypeMention condition, Type root
Term term, TypeAbstraction abs, TypeMention condition, Type root
) {
exists(TypePath path |
SatisfiesConstraintIsInstantiationOf::isNotInstantiationOf(tt, abs, condition, path) and
TermIsInstantiationOfCondition::isNotInstantiationOf(term, abs, condition, path) and
path.isCons(root.getATypeParameter(), _)
)
}
/**
* Holds if `tt` does not satisfy `constraint`.
* Holds if `term` does not satisfy `constraint`.
*
* This predicate is an approximation of `not hasConstraintMention(tt, constraint)`.
* This predicate is an approximation of `not hasConstraintMention(term, constraint)`.
*/
pragma[nomagic]
private predicate hasNotConstraintMention(HasTypeTree tt, Type constraint) {
exists(Type type | hasTypeConstraint(tt, type, constraint) |
private predicate hasNotConstraintMention(
Term term, Constraint constraint, Type constraintRoot
) {
exists(Type type | hasTypeConstraint(term, type, constraint, constraintRoot) |
// TODO: Handle universal conditions properly, which means taking type parameter constraints into account
// (
// exists(countConstraintImplementations(type, constraint))
@@ -921,15 +928,15 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
// )
// ) and
(
countConstraintImplementations(type, constraint) = 0
countConstraintImplementations(type, constraintRoot) = 0
or
not rootTypesSatisfaction(type, constraint, _, _, _)
not rootTypesSatisfaction(type, constraintRoot, _, _, _)
or
multipleConstraintImplementations(type, constraint) and
multipleConstraintImplementations(type, constraintRoot) and
forex(TypeAbstraction abs, TypeMention condition |
rootTypesSatisfaction(type, constraint, abs, condition, _)
rootTypesSatisfaction(type, constraintRoot, abs, condition, _)
|
isNotInstantiationOf(tt, abs, condition, type)
isNotInstantiationOf(term, abs, condition, type)
)
)
)
@@ -937,21 +944,22 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
pragma[nomagic]
private predicate satisfiesConstraintTypeMention0(
HasTypeTree tt, Type constraint, TypeAbstraction abs, TypeMention sub, TypePath path, Type t
Term term, Constraint constraint, TypeAbstraction abs, TypeMention sub, TypePath path,
Type t
) {
exists(TypeMention constraintMention |
hasConstraintMention(tt, abs, sub, constraint, constraintMention) and
exists(Type constraintRoot, TypeMention constraintMention |
hasConstraintMention(term, abs, sub, constraint, constraintRoot, constraintMention) and
conditionSatisfiesConstraintTypeAt(abs, sub, constraintMention, path, t)
)
}
pragma[inline]
private predicate satisfiesConstraintTypeMentionInline(
HasTypeTree tt, TypeAbstraction abs, Type constraint, TypePath path,
Term term, Constraint constraint, TypeAbstraction abs, TypePath path,
TypePath pathToTypeParamInSub
) {
exists(TypeMention sub, TypeParameter tp |
satisfiesConstraintTypeMention0(tt, constraint, abs, sub, path, tp) and
satisfiesConstraintTypeMention0(term, constraint, abs, sub, path, tp) and
tp = abs.getATypeParameter() and
sub.getTypeAt(pathToTypeParamInSub) = tp
)
@@ -959,91 +967,125 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
pragma[nomagic]
private predicate satisfiesConstraintTypeMention(
HasTypeTree tt, Type constraint, TypePath path, TypePath pathToTypeParamInSub
Term term, Constraint constraint, TypePath path, TypePath pathToTypeParamInSub
) {
satisfiesConstraintTypeMentionInline(tt, _, constraint, path, pathToTypeParamInSub)
satisfiesConstraintTypeMentionInline(term, constraint, _, path, pathToTypeParamInSub)
}
pragma[nomagic]
private predicate satisfiesConstraintTypeMentionThrough(
HasTypeTree tt, TypeAbstraction abs, Type constraint, TypePath path,
Term term, Constraint constraint, TypeAbstraction abs, TypePath path,
TypePath pathToTypeParamInSub
) {
satisfiesConstraintTypeMentionInline(tt, abs, constraint, path, pathToTypeParamInSub)
satisfiesConstraintTypeMentionInline(term, constraint, abs, path, pathToTypeParamInSub)
}
pragma[inline]
private predicate satisfiesConstraintTypeNonTypeParamInline(
HasTypeTree tt, TypeAbstraction abs, Type constraint, TypePath path, Type t
Term term, TypeAbstraction abs, Constraint constraint, TypePath path, Type t
) {
satisfiesConstraintTypeMention0(tt, constraint, abs, _, path, t) and
satisfiesConstraintTypeMention0(term, constraint, abs, _, path, t) and
not t = abs.getATypeParameter()
}
pragma[nomagic]
private predicate hasTypeConstraint(HasTypeTree term, Type constraint) {
hasTypeConstraint(term, constraint, constraint)
private predicate hasTypeConstraint(Term term, Constraint constraint) {
exists(Type constraintRoot |
hasTypeConstraint(term, constraintRoot, constraint, constraintRoot)
)
}
/**
* Holds if the type tree at `tt` satisfies the constraint `constraint`
* Holds if the type tree at `term` satisfies the constraint `constraint`
* with the type `t` at `path`.
*/
pragma[nomagic]
predicate satisfiesConstraintType(HasTypeTree tt, Type constraint, TypePath path, Type t) {
satisfiesConstraintTypeNonTypeParamInline(tt, _, constraint, path, t)
predicate satisfiesConstraintType(Term term, Constraint constraint, TypePath path, Type t) {
satisfiesConstraintTypeNonTypeParamInline(term, _, constraint, path, t)
or
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
satisfiesConstraintTypeMention(tt, constraint, prefix0, pathToTypeParamInSub) and
getTypeAt(tt, pathToTypeParamInSub.appendInverse(suffix)) = t and
satisfiesConstraintTypeMention(term, constraint, prefix0, pathToTypeParamInSub) and
getTypeAt(term, pathToTypeParamInSub.appendInverse(suffix)) = t and
path = prefix0.append(suffix)
)
or
hasTypeConstraint(tt, constraint) and
t = getTypeAt(tt, path)
hasTypeConstraint(term, constraint) and
t = getTypeAt(term, path)
}
/**
* Holds if the type tree at `tt` satisfies the constraint `constraint`
* Holds if the type tree at `term` satisfies the constraint `constraint`
* through `abs` with the type `t` at `path`.
*/
pragma[nomagic]
predicate satisfiesConstraintTypeThrough(
HasTypeTree tt, TypeAbstraction abs, Type constraint, TypePath path, Type t
Term term, TypeAbstraction abs, Constraint constraint, TypePath path, Type t
) {
satisfiesConstraintTypeNonTypeParamInline(tt, abs, constraint, path, t)
satisfiesConstraintTypeNonTypeParamInline(term, abs, constraint, path, t)
or
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
satisfiesConstraintTypeMentionThrough(tt, abs, constraint, prefix0, pathToTypeParamInSub) and
getTypeAt(tt, pathToTypeParamInSub.appendInverse(suffix)) = t and
satisfiesConstraintTypeMentionThrough(term, constraint, abs, prefix0, pathToTypeParamInSub) and
getTypeAt(term, pathToTypeParamInSub.appendInverse(suffix)) = t and
path = prefix0.append(suffix)
)
}
/**
* Holds if the type tree at `tt` does _not_ satisfy the constraint `constraint`.
* Holds if the type tree at `term` does _not_ satisfy the constraint `constraint`.
*
* This is an approximation of `not satisfiesConstraintType(tt, constraint, _, _)`,
* This is an approximation of `not satisfiesConstraintType(term, constraint, _, _)`,
* but defined without a negative occurrence of `satisfiesConstraintType`.
*
* Due to the approximation, both `satisfiesConstraintType` and `dissatisfiesConstraint`
* can hold for the same values. For example, if `tt` has two different types `t1`
* can hold for the same values. For example, if `term` has two different types `t1`
* and `t2`, and `t1` satisfies `constraint` while `t2` does not, then both
* `satisfiesConstraintType` and `dissatisfiesConstraint` will hold.
*
* Dually, if `tt` does not have a type, then neither `satisfiesConstraintType` nor
* Dually, if `term` does not have a type, then neither `satisfiesConstraintType` nor
* `dissatisfiesConstraint` will hold.
*/
pragma[nomagic]
predicate dissatisfiesConstraint(HasTypeTree tt, Type constraint) {
hasNotConstraintMention(tt, constraint) and
exists(Type t |
hasTypeConstraint(tt, t, constraint) and
t != constraint
predicate dissatisfiesConstraint(Term term, Constraint constraint) {
hasNotConstraintMention(term, constraint, _) and
exists(Type t, Type constraintRoot |
hasTypeConstraint(term, t, constraint, constraintRoot) and // todo
t != constraintRoot
)
}
}
/** Provides the input to `SatisfiesType`. */
signature module SatisfiesTypeInputSig<HasTypeTreeSig Term> {
/** Holds if it is relevant to know if `term` satisfies `type`. */
predicate relevantConstraint(Term term, Type type);
}
/**
* A helper module wrapping `SatisfiesConstraint` where the constraint is simply a type.
*/
module SatisfiesType<HasTypeTreeSig Term, SatisfiesTypeInputSig<Term> Input> {
private import Input
final private class TypeFinal = Type;
private class TypeAsTypeTree extends TypeFinal {
Type getTypeAt(TypePath path) {
result = this and
path.isEmpty()
}
}
private module SatisfiesConstraintInput implements
SatisfiesConstraintInputSig<Term, TypeAsTypeTree>
{
predicate relevantConstraint(Term term, TypeAsTypeTree constraint) {
Input::relevantConstraint(term, constraint)
}
}
import SatisfiesConstraint<Term, TypeAsTypeTree, SatisfiesConstraintInput>
}
/** Provides the input to `MatchingWithEnvironment`. */
signature module MatchingWithEnvironmentInputSig {
/**
@@ -1305,9 +1347,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
Location getLocation() { result = a.getLocation() }
}
private module SatisfiesConstraintInput implements
SatisfiesConstraintInputSig<RelevantAccess>
{
private module SatisfiesConstraintInput implements SatisfiesTypeInputSig<RelevantAccess> {
predicate relevantConstraint(RelevantAccess at, Type constraint) {
constraint = at.getConstraint(_)
}
@@ -1319,7 +1359,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
) {
exists(RelevantAccess ra |
ra = MkRelevantAccess(a, apos, e, prefix) and
SatisfiesConstraint<RelevantAccess, SatisfiesConstraintInput>::satisfiesConstraintType(ra,
SatisfiesType<RelevantAccess, SatisfiesConstraintInput>::satisfiesConstraintType(ra,
constraint, path, t) and
constraint = ra.getConstraint(target)
)