Override isCapturedAccess for self variables

Many `self` reads are synthesised from method calls with an implicit
`self` receiver. Synthesised nodes have no `toGenerated` result, which
the default definition of `isCapturedAccess` uses to determine if a
variable's scope matches the access's scope.

Hence we override the definition to properly identify accesses like the
call `puts` (below) as captured reads of a `self` variable defined in a
parent scope.

In other words, `puts x` is short for `self.puts x` and the `self`
refers to its value in the scope of the module `Foo`.

```ruby
module Foo
  MY_PROC = -> (x) { puts x }
end
```

We also have to update the SSA `SelfDefinition` to exclude captured
`self` variables.
This commit is contained in:
Harry Maclean
2021-10-21 15:28:14 +01:00
parent f1add388a0
commit 336bd15d2f
2 changed files with 15 additions and 4 deletions

View File

@@ -147,7 +147,7 @@ class LocalVariableAccess extends VariableAccess instanceof LocalVariableAccessI
* the access to `x` in the first `puts x` is a captured access, while
* the access to `x` in the second `puts x` is not.
*/
final predicate isCapturedAccess() { isCapturedAccess(this) }
predicate isCapturedAccess() { isCapturedAccess(this) }
}
/** An access to a local variable where the value is updated. */
@@ -195,4 +195,10 @@ class SelfVariableAccess extends LocalVariableAccess instanceof SelfVariableAcce
}
/** An access to the `self` variable where the value is read. */
class SelfVariableReadAccess extends SelfVariableAccess, VariableReadAccess { }
class SelfVariableReadAccess extends SelfVariableAccess, VariableReadAccess {
// We override the definition in `LocalVariableAccess` because it gives the
// wrong result for synthesised `self` variables.
override predicate isCapturedAccess() {
this.getVariable().getDeclaringScope() != this.getCfgScope()
}
}

View File

@@ -221,12 +221,17 @@ module Ssa {
}
/**
* An SSA definition that corresponds to the value of `self` upon method entry.
* An SSA definition that corresponds to the value of `self` upon entry to a method, class or module.
*/
class SelfDefinition extends Definition, SsaImplCommon::WriteDefinition {
private SelfVariable v;
SelfDefinition() { this.definesAt(v, _, _) }
SelfDefinition() {
exists(BasicBlock bb, int i |
this.definesAt(v, bb, i) and
not SsaImpl::capturedEntryWrite(bb, i, v)
)
}
final override string toString() { result = "self (" + v.getDeclaringScope() + ")" }