GH-725: fix logic for finding a lambda's usage of outer IDs

This commit is contained in:
Jon Siwek 2020-03-26 17:05:59 -07:00
parent f032885085
commit 1ca11f11c7
6 changed files with 43 additions and 22 deletions

View file

@ -4366,6 +4366,11 @@ LambdaExpr::LambdaExpr(std::unique_ptr<function_ingredients> arg_ing,
id->SetConst();
}
Scope* LambdaExpr::GetScope() const
{
return ingredients->scope.get();
}
IntrusivePtr<Val> LambdaExpr::Eval(Frame* f) const
{
auto lamb = make_intrusive<BroFunc>(

View file

@ -61,6 +61,7 @@ extern const char* expr_name(BroExprTag t);
template <class T> class IntrusivePtr;
class Stmt;
class Frame;
class Scope;
class ListExpr;
class NameExpr;
class IndexExpr;
@ -813,6 +814,8 @@ public:
IntrusivePtr<Val> Eval(Frame* f) const override;
TraversalCode Traverse(TraversalCallback* cb) const override;
Scope* GetScope() const;
protected:
void ExprDescribe(ODesc* d) const override;

View file

@ -12,7 +12,6 @@
#include "Scope.h"
#include "Type.h"
#include "File.h"
#include "Scope.h"
#include "Traverse.h"
#include "Val.h"
#include "zeekygen/Manager.h"

View file

@ -419,32 +419,25 @@ void begin_func(ID* id, const char* module_name, function_flavor flavor,
class OuterIDBindingFinder : public TraversalCallback {
public:
OuterIDBindingFinder(Scope* s)
: scope(s) { }
{
scopes.emplace_back(s);
}
virtual TraversalCode PreExpr(const Expr*);
virtual TraversalCode PostExpr(const Expr*);
Scope* scope;
std::vector<Scope*> scopes;
vector<const NameExpr*> outer_id_references;
int lambda_depth = 0;
// Note: think we really ought to toggle this to false to prevent
// considering locals within inner-lambdas as "outer", but other logic
// for "selective cloning" and locating IDs in the closure chain may
// depend on current behavior and also needs to be changed.
bool search_inner_lambdas = true;
};
TraversalCode OuterIDBindingFinder::PreExpr(const Expr* expr)
{
if ( expr->Tag() == EXPR_LAMBDA )
++lambda_depth;
if ( lambda_depth > 0 && ! search_inner_lambdas )
// Don't inspect the bodies of inner lambdas as they will have their
// own traversal to find outer IDs and we don't want to detect
// references to local IDs inside and accidentally treat them as
// "outer" since they can't be found in current scope.
{
auto le = static_cast<const LambdaExpr*>(expr);
scopes.emplace_back(le->GetScope());
return TC_CONTINUE;
}
if ( expr->Tag() != EXPR_NAME )
return TC_CONTINUE;
@ -454,7 +447,10 @@ TraversalCode OuterIDBindingFinder::PreExpr(const Expr* expr)
if ( e->Id()->IsGlobal() )
return TC_CONTINUE;
for ( const auto& scope : scopes )
if ( scope->Lookup(e->Id()->Name()) )
// Shadowing is not allowed, so if it's found at inner scope, it's
// not something we have to worry about also being at outer scope.
return TC_CONTINUE;
outer_id_references.push_back(e);
@ -464,10 +460,7 @@ TraversalCode OuterIDBindingFinder::PreExpr(const Expr* expr)
TraversalCode OuterIDBindingFinder::PostExpr(const Expr* expr)
{
if ( expr->Tag() == EXPR_LAMBDA )
{
--lambda_depth;
assert(lambda_depth >= 0);
}
scopes.pop_back();
return TC_CONTINUE;
}

View file

@ -0,0 +1,2 @@
106
106

View file

@ -0,0 +1,19 @@
# @TEST-EXEC: zeek -b %INPUT >out
# @TEST-EXEC: btest-diff out
local outer = 100;
local lambda = function()
{
local inner = function(a: count, b: count, c: count, d: count, e: count, f: count)
{
print outer + f;
};
inner(1, 2, 3, 4, 5, 6);
};
lambda();
local copyLambda = copy(copy(copy(lambda)));
copyLambda();