@@ -31,9 +31,52 @@ module PreSsa {
3131 }
3232
3333 predicate implicitEntryDef ( Callable c , SsaInput:: BasicBlock bb , SsaInput:: SourceVariable v ) {
34- not v instanceof LocalScopeVariable and
3534 c = v .getACallable ( ) and
36- scopeFirst ( c , bb )
35+ scopeFirst ( c , bb ) and
36+ (
37+ not v instanceof LocalScopeVariable
38+ or
39+ v .( SimpleLocalScopeVariable ) .isReadonlyCapturedBy ( c )
40+ )
41+ }
42+
43+ /** Holds if `a` is assigned in callable `c`. */
44+ pragma [ nomagic]
45+ private predicate assignableDefinition ( Assignable a , Callable c ) {
46+ exists ( AssignableDefinition def |
47+ def .getTarget ( ) = a and
48+ c = def .getEnclosingCallable ( )
49+ |
50+ not c instanceof Constructor or
51+ a instanceof LocalScopeVariable
52+ )
53+ }
54+
55+ pragma [ nomagic]
56+ private predicate assignableUniqueWriter ( Assignable a , Callable c ) {
57+ c = unique( Callable c0 | assignableDefinition ( a , c0 ) | c0 )
58+ }
59+
60+ /** Holds if `a` is accessed in callable `c`. */
61+ pragma [ nomagic]
62+ private predicate assignableAccess ( Assignable a , Callable c ) {
63+ exists ( AssignableAccess aa | aa .getTarget ( ) = a | c = aa .getEnclosingCallable ( ) )
64+ }
65+
66+ /**
67+ * A local scope variable that is amenable to SSA analysis.
68+ *
69+ * This is either a local variable that is not captured, or one
70+ * where all writes happen in the defining callable.
71+ */
72+ class SimpleLocalScopeVariable extends LocalScopeVariable {
73+ SimpleLocalScopeVariable ( ) { assignableUniqueWriter ( this , this .getCallable ( ) ) }
74+
75+ /** Holds if this local scope variable is read-only captured by `c`. */
76+ predicate isReadonlyCapturedBy ( Callable c ) {
77+ assignableAccess ( this , c ) and
78+ c != this .getCallable ( )
79+ }
3780 }
3881
3982 module SsaInput implements SsaImplCommon:: InputSig< Location > {
@@ -47,40 +90,6 @@ module PreSsa {
4790 ExitBasicBlock ( ) { scopeLast ( _, this .getLastElement ( ) , _) }
4891 }
4992
50- /** Holds if `a` is assigned in non-constructor callable `c`. */
51- pragma [ nomagic]
52- private predicate assignableDefinition ( Assignable a , Callable c ) {
53- exists ( AssignableDefinition def | def .getTarget ( ) = a |
54- c = def .getEnclosingCallable ( ) and
55- not c instanceof Constructor
56- )
57- }
58-
59- /** Holds if `a` is accessed in callable `c`. */
60- pragma [ nomagic]
61- private predicate assignableAccess ( Assignable a , Callable c ) {
62- exists ( AssignableAccess aa | aa .getTarget ( ) = a | c = aa .getEnclosingCallable ( ) )
63- }
64-
65- pragma [ nomagic]
66- private predicate assignableNoCapturing ( Assignable a , Callable c ) {
67- assignableAccess ( a , c ) and
68- /*
69- * The code below is equivalent to
70- * ```ql
71- * not exists(Callable other | assignableDefinition(a, other) | other != c)
72- * ```
73- * but it avoids a Cartesian product in the compiler generated antijoin
74- * predicate.
75- */
76-
77- (
78- not assignableDefinition ( a , _)
79- or
80- c = unique( Callable c0 | assignableDefinition ( a , c0 ) | c0 )
81- )
82- }
83-
8493 pragma [ noinline]
8594 private predicate assignableNoComplexQualifiers ( Assignable a ) {
8695 forall ( QualifiableExpr qe | qe .( AssignableAccess ) .getTarget ( ) = a | qe .targetIsThisInstance ( ) )
@@ -94,15 +103,22 @@ module PreSsa {
94103 private Callable c ;
95104
96105 SourceVariable ( ) {
106+ assignableAccess ( this , c ) and
97107 (
98- this instanceof LocalScopeVariable
108+ this instanceof SimpleLocalScopeVariable
99109 or
100- this = any ( Field f | not f .isVolatile ( ) )
101- or
102- this = any ( TrivialProperty tp | not tp .isOverridableOrImplementable ( ) )
103- ) and
104- assignableNoCapturing ( this , c ) and
105- assignableNoComplexQualifiers ( this )
110+ (
111+ this = any ( Field f | not f .isVolatile ( ) )
112+ or
113+ this = any ( TrivialProperty tp | not tp .isOverridableOrImplementable ( ) )
114+ ) and
115+ (
116+ not assignableDefinition ( this , _)
117+ or
118+ assignableUniqueWriter ( this , c )
119+ ) and
120+ assignableNoComplexQualifiers ( this )
121+ )
106122 }
107123
108124 /** Gets a callable in which this simple assignable can be analyzed. */
0 commit comments