Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor visit.rs with a Visitor trait #7081

Closed
pnkfelix opened this issue Jun 12, 2013 · 18 comments
Closed

Refactor visit.rs with a Visitor trait #7081

pnkfelix opened this issue Jun 12, 2013 · 18 comments

Comments

@pnkfelix
Copy link
Member

Currently libsyntax/visit.rs provides a record that holds a collection of @fn's, using a mix of lambda-lifting and an explicit virtual method table to define traversals over the AST.

pub struct Visitor<E> {
    visit_mod: @fn(&_mod, span, node_id, E, vt<E>),
    visit_view_item: @fn(@view_item, E, vt<E>),
    ...
}

where

pub enum vt<E> { mk_vt(visitor<E>), }

So then a use of the visitor looks like this (taken from freevars.rs):

    let v = visit::mk_vt(@visit::Visitor {visit_item: ignore_item,
                                          visit_expr: walk_expr,
                                          .. *visit::default_visitor()});
    (v.visit_block)(blk, 1, v);

where ignore_item and walk_expr are defined accordingly.

The above is awkward. A more natural way to encode this would be via a Visitor trait that defined the visit methods; then instances of the trait would correspond to the accumulated state denoted above by E in the struct definition and 1 in the freevars example.

(I originally had written a @Visitor object, but there is no need to commit to using objects in the Visitor interface.)

@pnkfelix
Copy link
Member Author

(The original description for this ticket used an @visitor object, but that was not what Niko intended. I'm rewriting the description now to reflect a more generic approach.)

@nikomatsakis
Copy link
Contributor

I don't think we should use objects here. I envision something like this:

trait Visitor {
    fn visit_expr(&mut self, expr: @ast::expr) {
        super_expr(self, expr);
    }
    fn visit_fn(&mut self, ...);
}

// It occurs to me that we have to break "super functions" into their own
// top-level functions if we want them to be callable.
fn super_expr<V:Visitor>(
    this: &mut V,
    expr: @ast::expr)
{
        match expr.node {
            ast::expr_binary(op, left, right) => {
                self.visit_expr(left); self.visit_expr(right);
            }
            ...
        }
}                                  

and then we implement individual passes follows:

struct MyContext {
    tcx: ty::ctxt, ...
}

impl visit::Visitor for MyContext {
    fn visit_expr(&mut self, expr: @ast::expr) {
        visit::super_expr(self, expr);
    }
}

@nikomatsakis
Copy link
Contributor

Incidentally, @msullivan has been working on fixing bugs in default methods. For all I know this approach may be feasible now, or there may be bugs standing in the way.

@ghost ghost assigned pnkfelix Jun 12, 2013
bors added a commit that referenced this issue Jun 12, 2013
This step 1 in my plan for issue #7081: Refactor visit.rs with a `@Visitor` trait.
@pnkfelix
Copy link
Member Author

Default methods, which block this, are tracked in #2794

@sanxiyn
Copy link
Member

sanxiyn commented Jul 4, 2013

Referencing @Aatch's branch here.

@pnkfelix
Copy link
Member Author

So apparently we have @pnkfelix working on this problem on branches labelled fsk-issue7081 like this, as well as @Aatch's branch that sanxiyn mentioned above, and now there is also @pcwalton working on a visit-trait branch as well, as he announced in the #rust irc channel.

Though I think pcwalton's code is putting in an @Trait style visitor; Niko previously pointed out on this isssue that it shouldn't be necessary to use an @Trait object here.

@pnkfelix
Copy link
Member Author

�One detail I meant to include in my last comment: the rewrite from my branch is attempting to support the transition from (E, vt<E>) to VisitorTrait in an incremental fashion, by putting in a impl<E> VisitorTrait for (E, vt<E>) as part of visit.rs.

I saw such incremental development as a virtue of my strategy, but its possible that getting that right is just slowing my progress down.

@nikomatsakis
Copy link
Contributor

Indeed. If we're going to rewrite all the visitor passes, it'd be a shame not to remove the managed boxes!

@huonw
Copy link
Member

huonw commented Jul 24, 2013

I guess having more info is useful: a while ago I used @Aatch's branch to start rewriting lint.

@pnkfelix
Copy link
Member Author

Okay, I've ported all of the visitor passes in a pretty mechanical fashion. The results of the mechanical rewrites are not pretty, nor are they meant to be.

I'm going to land this in a couple of incremental pull requests. The first few will be focused on landing the mechanical rewrites, with an emphasis on diffs that make it semi-obvious that they are refactorings. Code-cleanup will come later, on a file by file basis.

bors added a commit that referenced this issue Aug 15, 2013
…r=nikomatsakis

Rewriting visit.rs to operate on a borrowed `&mut V` where `<V:Visitor>`

r? @nikomatsakis
r? @pcwalton

This is the first in a planned series of incremental pull requests.  (There will probably be five pull requests including this one, though they can be combined or split as necessary.)

Part of #7081.  (But definitely does *not* complete it, not on its own, and not even after all five parts land; there are still a few loose ends to tie up or trim afterwards.)

The bulk of this change for this particular PR is pnkfelix/rust@3d83010, which has the changes necessary to visit.rs to support everything else that comes later.  The other commits are illustrating the standard mechanical transformation that I am applying.

One important point for nearly *all* of these pull requests: I was deliberately *not* trying to be intelligent in the transformation. 

 * My goal was to minimize code churn, and make the transformation as mechanical as possible.  
 * For example, I kept the separation between the Visitor struct (corresponding to the earlier vtable of functions that were potentially closed over local state) and the explicitly passed (and clones) visitor Env.  I am certain that this is almost always unnecessary, and a later task will be to go through an meld the Env's into the Visitors as appropriate.  (My original goal had been to make such melding part of this task; that's why I turned them into a (Env, vtable) tuple way back when.  But I digress.)
 * Also, my main goal here was to get rid of the record of `@fn`'s as described by the oldvisit.rs API.  (This series gets rid of all but one such case; I'm still investigating that.)  There is *still* plenty of `@`-boxing left to be removed, I'm sure, and even still some `@fn`'s too; removing all of those is not the goal here; its just to get rid of the encoded protocol of `@fn`'s in the (old)visit API.

To see where things will be going in the future (i.e., to get a sneak-preview of future pull-requests in the series), see:

 * https://github.com/pnkfelix/rust/commits/fsk-visitor-vpar-defaults-step1 (that's this one)
 * https://github.com/pnkfelix/rust/commits/fsk-visitor-vpar-defaults-step2
 * https://github.com/pnkfelix/rust/commits/fsk-visitor-vpar-defaults-step3
 * https://github.com/pnkfelix/rust/commits/fsk-visitor-vpar-defaults-step4
 * https://github.com/pnkfelix/rust/commits/fsk-visitor-vpar-defaults-step5
    * Note that between step 4 and step 5 there is just a single commit, but its a doozy because its the only case where my mechanical transformation did not apply, and thus more serious rewriting was necessary.  See commit pnkfelix/rust@da902b2
@pnkfelix
Copy link
Member Author

bors added a commit that referenced this issue Aug 19, 2013
…r=graydon,nikomatsakis

r? @nikomatsakis

Follow up to #8527 (which was step 1 of 5).  See that for overall description 

Part of #7081
bors added a commit that referenced this issue Aug 19, 2013
…r=nmatsakis

Follow up to #8539 (step 2 of 5).

(See  #8527, which was step 1 of 5, for the full outline.)

Part of #7081.
bors added a commit that referenced this issue Aug 19, 2013
…r=nmatsakis

Follow up to #8619 (step 3 of 5).

(See #8527, which was step 1 of 5, for the full outline.)

Part of #7081.
bors added a commit that referenced this issue Aug 20, 2013
…r=huonw

"non-mechanical" : there was lots more hacking than the other more-mechanical ports Felix did.

r? @huonw.  (Or @nikomatsakis ; I just want someone to sanity-check this.  Its not a thing of beauty.)

Followup to #8623.  (See #8527, which was step 1 of 5, for the full outline.  Part of #7081.)

Notes on the change follow.

There's also a strange pattern that I hacked in to accommodate the
Outer/Inner traversal structure of the existing code (which was
previously encoding this by untying the Y-combinator style knot of the
vtable, and then retying it but superimposing new methods that "stop
at items").  I hope either I or someone else can come back in the
future and replace this ugliness with something more natural.

Added boilerplate macro; all the OuterLint definitions are the same
(but must be abstracted over implementing struct, thus the macro).

Revised lint.rs use declarations to make ast references explicit.
Also removed unused imports.
@msullivan
Copy link
Contributor

So is this done now?

@pnkfelix
Copy link
Member Author

No it turns out there are a couple more oldvisit clients that need to be ported. One I knew about, the others I somehow overlooked before.

bors added a commit that referenced this issue Aug 26, 2013
Further followup on #7081.

There still remains writeback.rs, but I want to wait to investigate that one because I've seen `make check` issues with it in the past.
pnkfelix added a commit to pnkfelix/rust that referenced this issue Sep 10, 2013
pnkfelix added a commit to pnkfelix/rust that referenced this issue Sep 10, 2013
pnkfelix added a commit to pnkfelix/rust that referenced this issue Sep 10, 2013
pnkfelix added a commit to pnkfelix/rust that referenced this issue Sep 10, 2013
bors added a commit that referenced this issue Sep 10, 2013
r? anyone

Remove some trivial Visitor structs, using their non-trivial Contexts as the Visitor implementation instead.

Removed a little bit of `@boxing` as well.

Part of ongoing work on #7081.
@sanxiyn
Copy link
Member

sanxiyn commented Sep 13, 2013

Is this done now?

@pnkfelix
Copy link
Member Author

@sanxiyn I think it would be good to first finish unifying as many Ctxt+Visitors as possible, as illustrated in the commits with log messages of the form unify .*{C(on)?t(e)?xt,Visitor}

bors added a commit that referenced this issue Sep 24, 2013
…s, r=alexcrichton

r? anyone.

Part of #7081.

More refactorings of the syntax::visit::Visitor implementations, folding so-called "environments" into the visitor impl when the latter was previously a trivial unit struct.

As usual, this refactoring only applies when the environments are not actually carrying state that is meant to be pushed and popped as we traverse the expression.  (For an example where the environment *isn't* just passed through, see the `visit_fn` in `liveness.rs`.)

Got rid of a bit of @-allocation in borrowck.

Both cases should be pure-refactorings.
@pnkfelix
Copy link
Member Author

As a step towards closing this bug, here is a list of impls of visit::Visitor, and whether they have trivial environments (Visitor<()>) or cannot have trivial environments currently (with an explanation why in the latter case). (This is assuming PR #9453 lands.)

  • metadata/creader.rs:57:impl visit::Visitor<()> for ReadCrateVisitor
  • metadata/encoder.rs:1306:impl visit::Visitor<()> for EncodeVisitor
  • metadata/encoder.rs:1604:impl<'self> Visitor<()> for ImplVisitor<'self>
  • middle/borrowck/check_loans.rs:43:impl<'self> Visitor<()> for CheckLoanCtxt<'self>
  • middle/borrowck/gather_loans/mod.rs:76:impl<'self> visit::Visitor<()> for GatherLoanCtxt<'self>
  • middle/borrowck/mod.rs:64:impl Visitor<()> for BorrowckCtxt
  • middle/check_const.rs:32:impl Visitor<bool> for CheckCrateVisitor : is_const changes as visitor walks.
  • middle/check_const.rs:249:impl Visitor<env> for CheckItemRecursionVisitor: the idstack is pushed/popped, but it seems like it is passed linearly and could still be folded into the visitor itself. To do.
  • middle/check_loop.rs:34:impl Visitor<Context> for CheckLoopVisitor : the Context changes as visitor walks.
  • middle/check_match.rs:41:impl Visitor<()> for CheckMatchVisitor
  • middle/const_eval.rs:273:impl Visitor<()> for ConstEvalVisitor
  • middle/effect.rs:74:impl Visitor<()> for EffectCheckVisitor
  • middle/entry.rs:41:impl Visitor<()> for EntryContext
  • middle/freevars.rs:41:impl Visitor<int> for CollectFreevarsVisitor : the depth increases as the visitor descends.
  • middle/freevars.rs:111:impl Visitor<()> for AnnotateFreevarsVisitor
  • middle/kind.rs:62:impl Visitor<Context> for KindAnalysisVisitor: there is a current_item field in Context that is updated contextually, but AFAICT it is never used. If we remove it then we can make the Context the Visitor.
  • middle/lang_items.rs:308:impl<'self> Visitor<()> for LanguageItemVisitor<'self>
  • middle/liveness.rs:156:impl Visitor<@mut IrMaps> for LivenessVisitor : the IrMaps get new maps swapped in, e.g. in visit_fn
  • middle/liveness.rs:350:impl Visitor<@Liveness> for ErrorCheckVisitor
  • middle/moves.rs:194:impl visit::Visitor<()> for VisitContext
  • middle/privacy.rs:344:impl<'self> Visitor<&'self method_map> for PrivacyVisitor
  • middle/reachable.rs:102:impl Visitor<PrivacyContext> for ReachableVisitor : the privacy_context changes as the visitor walks,to track whether we are in a pub or priv context.
  • middle/reachable.rs:206:impl Visitor<()> for MarkSymbolVisitor
  • middle/region.rs:475:impl Visitor<Context> for RegionResolutionVisitor
  • middle/region.rs:911:impl Visitor<@mut DetermineRpCtxt> for DetermineRpVisitor
  • middle/resolve.rs:142:impl Visitor<()> for ResolveVisitor
  • middle/resolve.rs:928:impl Visitor<ReducedGraphParent> for BuildReducedGraphVisitor : the parent changes as the visitor walks. Code probably could use cleanup, but not as part of this ticket.
  • middle/resolve.rs:956:impl Visitor<()> for UnusedImportCheckVisitor
  • middle/stack_check.rs:37:impl Visitor<Context> for StackCheckVisitor : stack changes as the visitor walks. Still the ty::ctxt probably could be migrated over to the StackCheckVisitor.
  • middle/trans/base.rs:2199:impl Visitor<@mut CrateContext> for TransItemVisitor
  • middle/trans/callee.rs:575:impl Visitor<@mut bool> for CalleeTranslationVisitor
  • middle/trans/debuginfo.rs:2759:impl<'self> visit::Visitor<()> for NamespaceVisitor<'self>
  • middle/trans/type_use.rs:419:impl<'self> Visitor<&'self Context> for TypeUseVisitor
  • middle/typeck/check/mod.rs:302:impl Visitor<()> for CheckItemTypesVisitor
  • middle/typeck/check/mod.rs:358:impl Visitor<()> for GatherLocalsVisitor
  • middle/typeck/coherence.rs:160:impl visit::Visitor<()> for CoherenceCheckVisitor
  • middle/typeck/coherence.rs:185:impl visit::Visitor<()> for PrivilegedScopeVisitor
  • middle/typeck/collect.rs:69:impl visit::Visitor<()> for CollectItemTypesVisitor
  • util/common.rs:67:impl Visitor<@mut bool> for LoopQueryVisitor (flag changes as we traverse, but may still be passed linearly)
  • util/common.rs:92:impl Visitor<@mut bool> for BlockQueryVisitor (flag changes as we traverse, but may still be passed linearly)

I spent a while this morning/afternoon looking at the typeck/regionck cases below. Unfortunately the assumption that we have a @mut FnCtxt goes pretty deep into the code, and the most immediate refactorings I applied hit some snags, the first being in vtable::early_resolve_expr, where we have a loop over type substitutions in the fcx while we also modify the vtables of the fcx. (And that is just the first snag.) So resolving @mut FnCtxt properly may be a worthy of a separate ticket. (Then again, the more conservative approach of just getting rid of VtableResolveVisitor, without worrying about the @mut itself, may still be viable.) I'll look at the others indendent of FnCtxt later.

  • middle/typeck/check/regionck.rs:167:impl Visitor<@mut Rcx> for RegionckVisitor
  • middle/typeck/check/vtable.rs:776:impl visit::Visitor<@mut FnCtxt> for VtableResolveVisitor
  • middle/typeck/check/writeback.rs:323:impl Visitor<@mut WbCtxt> for WbVisitor

The lints are a special case; I suspect this code needs a rewrite anyway, and its probably not worth trying to further bandaid it with refactorings like this. Still, here's the list.

  • middle/lint.rs:691:impl Visitor<@mut Context> for WhileTrueLintVisitor
  • middle/lint.rs:854:impl Visitor<@mut Context> for TypeLimitsLintVisitor
  • middle/lint.rs:995:impl Visitor<@mut Context> for HeapLintVisitor
  • middle/lint.rs:1026:impl Visitor<@mut Context> for PathStatementLintVisitor
  • middle/lint.rs:1119:impl Visitor<@mut Context> for UnusedUnsafeLintVisitor
  • middle/lint.rs:1191:impl Visitor<@mut Context> for UnusedMutLintVisitor
  • middle/lint.rs:1248:impl Visitor<@mut Context> for UnnecessaryAllocationLintVisitor
  • middle/lint.rs:1322:impl Visitor<@mut Context> for MissingDocLintVisitor
  • middle/lint.rs:1472:impl Visitor<@mut Context> for StabilityLintVisitor
  • middle/lint.rs:1507:impl Visitor<@mut Context> for LintCheckVisitor

pnkfelix added a commit to pnkfelix/rust that referenced this issue Sep 24, 2013
bors added a commit that referenced this issue Sep 24, 2013
…-typeck, r=huonw

r? anyone

Also got rid of a bit of `@mut` allocation.  (Though not the monster that is `@mut FnCtxt`; that case is documented already on #7081; if we attack it, it will probably be its own ticket, not part of #7081.)
bors added a commit that referenced this issue Sep 25, 2013
…er, r=huonw

r? anyone

Part of #7081.

Removed many unnecessary context arguments, turning them into visitors.  Removed some @allocation.

If this lands, then I think the only thing left that is unaddressed are:
 * the various lint visitors, and
 * middle/privacy.rs, which has `impl<'self> Visitor<&'self method_map> for PrivacyVisitor`
@pnkfelix
Copy link
Member Author

okay, looks like just the privacy.rs and the lints are left.

And privacy isn't as hard to deal with as I had thought it would be (I should have tried harder to get it into that last PR). I'll probably close this ticket after I put up a PR for privacy.

@pnkfelix
Copy link
Member Author

@alexcrichton is going to be mucking with the privacy code, so I'm going to yield that battle ground to him and close this ticket. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants