Auto merge of #33296 - jseyfried:non_idempotent_lowering, r=nrc

Remove the requirement that ast->hir lowering be reproducible

This PR changes the ast->hir lowerer to be non-reproducible, and it removes the lowering context's id cache.

If the `hir` of an `ast` node needs to be reproduced, we can use the hir map instead of the lowerer -- for example, `tcx.map.expect_expr(expr.id)` instead of `lower_expr(lcx, expr)`.

r? @nrc
This commit is contained in:
bors
2016-05-01 18:28:24 -07:00
5 changed files with 349 additions and 601 deletions
+337 -570
View File
@@ -29,27 +29,6 @@
// are unique). Every new node must have a unique id. Avoid cloning HIR nodes.
// If you do, you must then set the new node's id to a fresh one.
//
// Lowering must be reproducable (the compiler only lowers once, but tools and
// custom lints may lower an AST node to a HIR node to interact with the
// compiler). The most interesting bit of this is ids - if you lower an AST node
// and create new HIR nodes with fresh ids, when re-lowering the same node, you
// must ensure you get the same ids! To do this, we keep track of the next id
// when we translate a node which requires new ids. By checking this cache and
// using node ids starting with the cached id, we ensure ids are reproducible.
// To use this system, you just need to hold on to a CachedIdSetter object
// whilst lowering. This is an RAII object that takes care of setting and
// restoring the cached id, etc.
//
// This whole system relies on node ids being incremented one at a time and
// all increments being for lowering. This means that you should not call any
// non-lowering function which will use new node ids.
//
// We must also cache gensym'ed Idents to ensure that we get the same Ident
// every time we lower a node with gensym'ed names. One consequence of this is
// that you can only gensym a name once in a lowering (you don't need to worry
// about nested lowering though). That's because we cache based on the name and
// the currently cached node id, which is unique per lowered node.
//
// Spans are used for error messages and for tools to map semantics back to
// source code. It is therefore not as important with spans as ids to be strict
// about use (you can't break the compiler by screwing up a span). Obviously, a
@@ -67,7 +46,6 @@
use hir::def_id::DefIndex;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::iter;
use syntax::ast::*;
use syntax::attr::{ThinAttributes, ThinAttributesExt};
@@ -83,18 +61,8 @@
pub struct LoweringContext<'a> {
crate_root: Option<&'static str>,
// Map AST ids to ids used for expanded nodes.
id_cache: RefCell<HashMap<NodeId, NodeId>>,
// Use if there are no cached ids for the current node.
// Use to assign ids to hir nodes that do not directly correspond to an ast node
id_assigner: &'a NodeIdAssigner,
// 0 == no cached id. Must be incremented to align with previous id
// incrementing.
cached_id: Cell<u32>,
// Keep track of gensym'ed idents.
gensym_cache: RefCell<HashMap<(NodeId, &'static str), hir::Ident>>,
// A copy of cached_id, but is also set to an id while a node is lowered for
// the first time.
gensym_key: Cell<u32>,
// We must keep the set of definitions up to date as we add nodes that
// weren't in the AST.
definitions: Option<&'a RefCell<Definitions>>,
@@ -121,11 +89,7 @@ pub fn new(id_assigner: &'a NodeIdAssigner,
LoweringContext {
crate_root: crate_root,
id_cache: RefCell::new(HashMap::new()),
id_assigner: id_assigner,
cached_id: Cell::new(0),
gensym_cache: RefCell::new(HashMap::new()),
gensym_key: Cell::new(0),
definitions: Some(defs),
parent_def: Cell::new(None),
}
@@ -136,40 +100,18 @@ pub fn new(id_assigner: &'a NodeIdAssigner,
pub fn testing_context(id_assigner: &'a NodeIdAssigner) -> LoweringContext<'a> {
LoweringContext {
crate_root: None,
id_cache: RefCell::new(HashMap::new()),
id_assigner: id_assigner,
cached_id: Cell::new(0),
gensym_cache: RefCell::new(HashMap::new()),
gensym_key: Cell::new(0),
definitions: None,
parent_def: Cell::new(None),
}
}
fn next_id(&self) -> NodeId {
let cached_id = self.cached_id.get();
if cached_id == 0 {
return self.id_assigner.next_node_id();
}
self.cached_id.set(cached_id + 1);
cached_id
self.id_assigner.next_node_id()
}
fn str_to_ident(&self, s: &'static str) -> hir::Ident {
let gensym_key = self.gensym_key.get();
if gensym_key == 0 {
return hir::Ident::from_name(token::gensym(s));
}
let cached = self.gensym_cache.borrow().contains_key(&(gensym_key, s));
if cached {
self.gensym_cache.borrow()[&(gensym_key, s)]
} else {
let result = hir::Ident::from_name(token::gensym(s));
self.gensym_cache.borrow_mut().insert((gensym_key, s), result);
result
}
hir::Ident::from_name(token::gensym(s))
}
// Panics if this LoweringContext's NodeIdAssigner is not able to emit diagnostics.
@@ -197,53 +139,6 @@ fn get_def(&self, id: NodeId) -> DefIndex {
}
}
// Utility fn for setting and unsetting the cached id.
fn cache_ids<'a, OP, R>(lctx: &LoweringContext, expr_id: NodeId, op: OP) -> R
where OP: FnOnce(&LoweringContext) -> R
{
// Only reset the id if it was previously 0, i.e., was not cached.
// If it was cached, we are in a nested node, but our id count will
// still count towards the parent's count.
let reset_cached_id = lctx.cached_id.get() == 0;
// We always reset gensym_key so that if we use the same name in a nested
// node and after that node, they get different values.
let old_gensym_key = lctx.gensym_key.get();
{
let id_cache: &mut HashMap<_, _> = &mut lctx.id_cache.borrow_mut();
if id_cache.contains_key(&expr_id) {
let cached_id = lctx.cached_id.get();
if cached_id == 0 {
// We're entering a node where we need to track ids, but are not
// yet tracking.
lctx.cached_id.set(id_cache[&expr_id]);
} else {
// We're already tracking - check that the tracked id is the same
// as the expected id.
assert!(cached_id == id_cache[&expr_id], "id mismatch");
}
lctx.gensym_key.set(id_cache[&expr_id]);
} else {
// We've never lowered this node before, remember it for next time.
let next_id = lctx.id_assigner.peek_node_id();
id_cache.insert(expr_id, next_id);
lctx.gensym_key.set(next_id);
// self.cached_id is not set when we lower a node for the first time,
// only on re-lowering.
}
}
let result = op(lctx);
if reset_cached_id {
lctx.cached_id.set(0);
}
lctx.gensym_key.set(old_gensym_key);
result
}
pub fn lower_ident(_lctx: &LoweringContext, ident: Ident) -> hir::Ident {
hir::Ident {
name: mtwt::resolve(ident),
@@ -1077,102 +972,100 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
// std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR ));
// InPlace::finalize(place)
// })
return cache_ids(lctx, e.id, |lctx| {
let placer_expr = lower_expr(lctx, placer);
let value_expr = lower_expr(lctx, value_expr);
let placer_expr = lower_expr(lctx, placer);
let value_expr = lower_expr(lctx, value_expr);
let placer_ident = lctx.str_to_ident("placer");
let place_ident = lctx.str_to_ident("place");
let p_ptr_ident = lctx.str_to_ident("p_ptr");
let placer_ident = lctx.str_to_ident("placer");
let place_ident = lctx.str_to_ident("place");
let p_ptr_ident = lctx.str_to_ident("p_ptr");
let make_place = ["ops", "Placer", "make_place"];
let place_pointer = ["ops", "Place", "pointer"];
let move_val_init = ["intrinsics", "move_val_init"];
let inplace_finalize = ["ops", "InPlace", "finalize"];
let make_place = ["ops", "Placer", "make_place"];
let place_pointer = ["ops", "Place", "pointer"];
let move_val_init = ["intrinsics", "move_val_init"];
let inplace_finalize = ["ops", "InPlace", "finalize"];
let make_call = |lctx: &LoweringContext, p, args| {
let path = core_path(lctx, e.span, p);
let path = expr_path(lctx, path, None);
expr_call(lctx, e.span, path, args, None)
};
let make_call = |lctx: &LoweringContext, p, args| {
let path = core_path(lctx, e.span, p);
let path = expr_path(lctx, path, None);
expr_call(lctx, e.span, path, args, None)
};
let mk_stmt_let = |lctx: &LoweringContext, bind, expr| {
stmt_let(lctx, e.span, false, bind, expr, None)
};
let mk_stmt_let = |lctx: &LoweringContext, bind, expr| {
stmt_let(lctx, e.span, false, bind, expr, None)
};
let mk_stmt_let_mut = |lctx: &LoweringContext, bind, expr| {
stmt_let(lctx, e.span, true, bind, expr, None)
};
let mk_stmt_let_mut = |lctx: &LoweringContext, bind, expr| {
stmt_let(lctx, e.span, true, bind, expr, None)
};
// let placer = <placer_expr> ;
let s1 = {
let placer_expr = signal_block_expr(lctx,
hir_vec![],
placer_expr,
e.span,
hir::PopUnstableBlock,
None);
mk_stmt_let(lctx, placer_ident, placer_expr)
};
// let placer = <placer_expr> ;
let s1 = {
let placer_expr = signal_block_expr(lctx,
hir_vec![],
placer_expr,
e.span,
hir::PopUnstableBlock,
None);
mk_stmt_let(lctx, placer_ident, placer_expr)
};
// let mut place = Placer::make_place(placer);
let s2 = {
let placer = expr_ident(lctx, e.span, placer_ident, None);
let call = make_call(lctx, &make_place, hir_vec![placer]);
mk_stmt_let_mut(lctx, place_ident, call)
};
// let mut place = Placer::make_place(placer);
let s2 = {
let placer = expr_ident(lctx, e.span, placer_ident, None);
let call = make_call(lctx, &make_place, hir_vec![placer]);
mk_stmt_let_mut(lctx, place_ident, call)
};
// let p_ptr = Place::pointer(&mut place);
let s3 = {
let agent = expr_ident(lctx, e.span, place_ident, None);
let args = hir_vec![expr_mut_addr_of(lctx, e.span, agent, None)];
let call = make_call(lctx, &place_pointer, args);
mk_stmt_let(lctx, p_ptr_ident, call)
};
// pop_unsafe!(EXPR));
let pop_unsafe_expr = {
let value_expr = signal_block_expr(lctx,
hir_vec![],
value_expr,
e.span,
hir::PopUnstableBlock,
None);
signal_block_expr(lctx,
hir_vec![],
value_expr,
e.span,
hir::PopUnsafeBlock(hir::CompilerGenerated), None)
};
// push_unsafe!({
// std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR ));
// InPlace::finalize(place)
// })
let expr = {
let ptr = expr_ident(lctx, e.span, p_ptr_ident, None);
let call_move_val_init =
hir::StmtSemi(
make_call(lctx, &move_val_init, hir_vec![ptr, pop_unsafe_expr]),
lctx.next_id());
let call_move_val_init = respan(e.span, call_move_val_init);
let place = expr_ident(lctx, e.span, place_ident, None);
let call = make_call(lctx, &inplace_finalize, hir_vec![place]);
signal_block_expr(lctx,
hir_vec![call_move_val_init],
call,
e.span,
hir::PushUnsafeBlock(hir::CompilerGenerated), None)
};
// let p_ptr = Place::pointer(&mut place);
let s3 = {
let agent = expr_ident(lctx, e.span, place_ident, None);
let args = hir_vec![expr_mut_addr_of(lctx, e.span, agent, None)];
let call = make_call(lctx, &place_pointer, args);
mk_stmt_let(lctx, p_ptr_ident, call)
};
// pop_unsafe!(EXPR));
let pop_unsafe_expr = {
let value_expr = signal_block_expr(lctx,
hir_vec![],
value_expr,
e.span,
hir::PopUnstableBlock,
None);
signal_block_expr(lctx,
hir_vec![s1, s2, s3],
expr,
hir_vec![],
value_expr,
e.span,
hir::PushUnstableBlock,
e.attrs.clone())
});
hir::PopUnsafeBlock(hir::CompilerGenerated), None)
};
// push_unsafe!({
// std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR ));
// InPlace::finalize(place)
// })
let expr = {
let ptr = expr_ident(lctx, e.span, p_ptr_ident, None);
let call_move_val_init =
hir::StmtSemi(
make_call(lctx, &move_val_init, hir_vec![ptr, pop_unsafe_expr]),
lctx.next_id());
let call_move_val_init = respan(e.span, call_move_val_init);
let place = expr_ident(lctx, e.span, place_ident, None);
let call = make_call(lctx, &inplace_finalize, hir_vec![place]);
signal_block_expr(lctx,
hir_vec![call_move_val_init],
call,
e.span,
hir::PushUnsafeBlock(hir::CompilerGenerated), None)
};
return signal_block_expr(lctx,
hir_vec![s1, s2, s3],
expr,
e.span,
hir::PushUnstableBlock,
e.attrs.clone());
}
ExprKind::Vec(ref exprs) => {
@@ -1226,20 +1119,18 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
let else_opt = else_opt.as_ref().map(|els| {
match els.node {
ExprKind::IfLet(..) => {
cache_ids(lctx, e.id, |lctx| {
// wrap the if-let expr in a block
let span = els.span;
let els = lower_expr(lctx, els);
let id = lctx.next_id();
let blk = P(hir::Block {
stmts: hir_vec![],
expr: Some(els),
id: id,
rules: hir::DefaultBlock,
span: span,
});
expr_block(lctx, blk, None)
})
// wrap the if-let expr in a block
let span = els.span;
let els = lower_expr(lctx, els);
let id = lctx.next_id();
let blk = P(hir::Block {
stmts: hir_vec![],
expr: Some(els),
id: id,
rules: hir::DefaultBlock,
span: span,
});
expr_block(lctx, blk, None)
}
_ => lower_expr(lctx, els),
}
@@ -1328,38 +1219,36 @@ fn make_struct(lctx: &LoweringContext,
None)
}
return cache_ids(lctx, e.id, |lctx| {
use syntax::ast::RangeLimits::*;
use syntax::ast::RangeLimits::*;
match (e1, e2, lims) {
(&None, &None, HalfOpen) =>
make_struct(lctx, e, &["RangeFull"],
&[]),
return match (e1, e2, lims) {
(&None, &None, HalfOpen) =>
make_struct(lctx, e, &["RangeFull"],
&[]),
(&Some(ref e1), &None, HalfOpen) =>
make_struct(lctx, e, &["RangeFrom"],
&[("start", e1)]),
(&Some(ref e1), &None, HalfOpen) =>
make_struct(lctx, e, &["RangeFrom"],
&[("start", e1)]),
(&None, &Some(ref e2), HalfOpen) =>
make_struct(lctx, e, &["RangeTo"],
&[("end", e2)]),
(&None, &Some(ref e2), HalfOpen) =>
make_struct(lctx, e, &["RangeTo"],
&[("end", e2)]),
(&Some(ref e1), &Some(ref e2), HalfOpen) =>
make_struct(lctx, e, &["Range"],
&[("start", e1), ("end", e2)]),
(&Some(ref e1), &Some(ref e2), HalfOpen) =>
make_struct(lctx, e, &["Range"],
&[("start", e1), ("end", e2)]),
(&None, &Some(ref e2), Closed) =>
make_struct(lctx, e, &["RangeToInclusive"],
&[("end", e2)]),
(&None, &Some(ref e2), Closed) =>
make_struct(lctx, e, &["RangeToInclusive"],
&[("end", e2)]),
(&Some(ref e1), &Some(ref e2), Closed) =>
make_struct(lctx, e, &["RangeInclusive", "NonEmpty"],
&[("start", e1), ("end", e2)]),
(&Some(ref e1), &Some(ref e2), Closed) =>
make_struct(lctx, e, &["RangeInclusive", "NonEmpty"],
&[("start", e1), ("end", e2)]),
_ => panic!(lctx.diagnostic().span_fatal(e.span,
"inclusive range with no end"))
}
});
_ => panic!(lctx.diagnostic().span_fatal(e.span,
"inclusive range with no end"))
};
}
ExprKind::Path(ref qself, ref path) => {
let hir_qself = qself.as_ref().map(|&QSelf { ref ty, position }| {
@@ -1433,81 +1322,79 @@ fn make_struct(lctx: &LoweringContext,
// _ => [<else_opt> | ()]
// }
return cache_ids(lctx, e.id, |lctx| {
// `<pat> => <body>`
let pat_arm = {
let body = lower_block(lctx, body);
let body_expr = expr_block(lctx, body, None);
arm(hir_vec![lower_pat(lctx, pat)], body_expr)
};
// `<pat> => <body>`
let pat_arm = {
let body = lower_block(lctx, body);
let body_expr = expr_block(lctx, body, None);
arm(hir_vec![lower_pat(lctx, pat)], body_expr)
};
// `[_ if <else_opt_if_cond> => <else_opt_if_body>,]`
let mut else_opt = else_opt.as_ref().map(|e| lower_expr(lctx, e));
let else_if_arms = {
let mut arms = vec![];
loop {
let else_opt_continue = else_opt.and_then(|els| {
els.and_then(|els| {
match els.node {
// else if
hir::ExprIf(cond, then, else_opt) => {
let pat_under = pat_wild(lctx, e.span);
arms.push(hir::Arm {
attrs: hir_vec![],
pats: hir_vec![pat_under],
guard: Some(cond),
body: expr_block(lctx, then, None),
});
else_opt.map(|else_opt| (else_opt, true))
}
_ => Some((P(els), false)),
// `[_ if <else_opt_if_cond> => <else_opt_if_body>,]`
let mut else_opt = else_opt.as_ref().map(|e| lower_expr(lctx, e));
let else_if_arms = {
let mut arms = vec![];
loop {
let else_opt_continue = else_opt.and_then(|els| {
els.and_then(|els| {
match els.node {
// else if
hir::ExprIf(cond, then, else_opt) => {
let pat_under = pat_wild(lctx, e.span);
arms.push(hir::Arm {
attrs: hir_vec![],
pats: hir_vec![pat_under],
guard: Some(cond),
body: expr_block(lctx, then, None),
});
else_opt.map(|else_opt| (else_opt, true))
}
})
});
match else_opt_continue {
Some((e, true)) => {
else_opt = Some(e);
}
Some((e, false)) => {
else_opt = Some(e);
break;
}
None => {
else_opt = None;
break;
_ => Some((P(els), false)),
}
})
});
match else_opt_continue {
Some((e, true)) => {
else_opt = Some(e);
}
Some((e, false)) => {
else_opt = Some(e);
break;
}
None => {
else_opt = None;
break;
}
}
arms
};
}
arms
};
let contains_else_clause = else_opt.is_some();
let contains_else_clause = else_opt.is_some();
// `_ => [<else_opt> | ()]`
let else_arm = {
let pat_under = pat_wild(lctx, e.span);
let else_expr =
else_opt.unwrap_or_else(
|| expr_tuple(lctx, e.span, hir_vec![], None));
arm(hir_vec![pat_under], else_expr)
};
// `_ => [<else_opt> | ()]`
let else_arm = {
let pat_under = pat_wild(lctx, e.span);
let else_expr =
else_opt.unwrap_or_else(
|| expr_tuple(lctx, e.span, hir_vec![], None));
arm(hir_vec![pat_under], else_expr)
};
let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
arms.push(pat_arm);
arms.extend(else_if_arms);
arms.push(else_arm);
let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
arms.push(pat_arm);
arms.extend(else_if_arms);
arms.push(else_arm);
let sub_expr = lower_expr(lctx, sub_expr);
// add attributes to the outer returned expr node
expr(lctx,
e.span,
hir::ExprMatch(sub_expr,
arms.into(),
hir::MatchSource::IfLetDesugar {
contains_else_clause: contains_else_clause,
}),
e.attrs.clone())
});
let sub_expr = lower_expr(lctx, sub_expr);
// add attributes to the outer returned expr node
return expr(lctx,
e.span,
hir::ExprMatch(sub_expr,
arms.into(),
hir::MatchSource::IfLetDesugar {
contains_else_clause: contains_else_clause,
}),
e.attrs.clone());
}
// Desugar ExprWhileLet
@@ -1522,38 +1409,36 @@ fn make_struct(lctx: &LoweringContext,
// }
// }
return cache_ids(lctx, e.id, |lctx| {
// `<pat> => <body>`
let pat_arm = {
let body = lower_block(lctx, body);
let body_expr = expr_block(lctx, body, None);
arm(hir_vec![lower_pat(lctx, pat)], body_expr)
};
// `<pat> => <body>`
let pat_arm = {
let body = lower_block(lctx, body);
let body_expr = expr_block(lctx, body, None);
arm(hir_vec![lower_pat(lctx, pat)], body_expr)
};
// `_ => break`
let break_arm = {
let pat_under = pat_wild(lctx, e.span);
let break_expr = expr_break(lctx, e.span, None);
arm(hir_vec![pat_under], break_expr)
};
// `_ => break`
let break_arm = {
let pat_under = pat_wild(lctx, e.span);
let break_expr = expr_break(lctx, e.span, None);
arm(hir_vec![pat_under], break_expr)
};
// `match <sub_expr> { ... }`
let arms = hir_vec![pat_arm, break_arm];
let sub_expr = lower_expr(lctx, sub_expr);
let match_expr = expr(lctx,
e.span,
hir::ExprMatch(sub_expr,
arms,
hir::MatchSource::WhileLetDesugar),
None);
// `match <sub_expr> { ... }`
let arms = hir_vec![pat_arm, break_arm];
let sub_expr = lower_expr(lctx, sub_expr);
let match_expr = expr(lctx,
e.span,
hir::ExprMatch(sub_expr,
arms,
hir::MatchSource::WhileLetDesugar),
None);
// `[opt_ident]: loop { ... }`
let loop_block = block_expr(lctx, match_expr);
let loop_expr = hir::ExprLoop(loop_block,
opt_ident.map(|ident| lower_ident(lctx, ident)));
// add attributes to the outer returned expr node
expr(lctx, e.span, loop_expr, e.attrs.clone())
});
// `[opt_ident]: loop { ... }`
let loop_block = block_expr(lctx, match_expr);
let loop_expr = hir::ExprLoop(loop_block,
opt_ident.map(|ident| lower_ident(lctx, ident)));
// add attributes to the outer returned expr node
return expr(lctx, e.span, loop_expr, e.attrs.clone());
}
// Desugar ExprForLoop
@@ -1575,106 +1460,104 @@ fn make_struct(lctx: &LoweringContext,
// result
// }
return cache_ids(lctx, e.id, |lctx| {
// expand <head>
let head = lower_expr(lctx, head);
// expand <head>
let head = lower_expr(lctx, head);
let iter = lctx.str_to_ident("iter");
let iter = lctx.str_to_ident("iter");
// `::std::option::Option::Some(<pat>) => <body>`
let pat_arm = {
let body_block = lower_block(lctx, body);
let body_span = body_block.span;
let body_expr = P(hir::Expr {
id: lctx.next_id(),
node: hir::ExprBlock(body_block),
span: body_span,
attrs: None,
});
let pat = lower_pat(lctx, pat);
let some_pat = pat_some(lctx, e.span, pat);
// `::std::option::Option::Some(<pat>) => <body>`
let pat_arm = {
let body_block = lower_block(lctx, body);
let body_span = body_block.span;
let body_expr = P(hir::Expr {
id: lctx.next_id(),
node: hir::ExprBlock(body_block),
span: body_span,
attrs: None,
});
let pat = lower_pat(lctx, pat);
let some_pat = pat_some(lctx, e.span, pat);
arm(hir_vec![some_pat], body_expr)
arm(hir_vec![some_pat], body_expr)
};
// `::std::option::Option::None => break`
let break_arm = {
let break_expr = expr_break(lctx, e.span, None);
arm(hir_vec![pat_none(lctx, e.span)], break_expr)
};
// `match ::std::iter::Iterator::next(&mut iter) { ... }`
let match_expr = {
let next_path = {
let strs = std_path(lctx, &["iter", "Iterator", "next"]);
path_global(e.span, strs)
};
let iter = expr_ident(lctx, e.span, iter, None);
let ref_mut_iter = expr_mut_addr_of(lctx, e.span, iter, None);
let next_path = expr_path(lctx, next_path, None);
let next_expr = expr_call(lctx,
e.span,
next_path,
hir_vec![ref_mut_iter],
None);
let arms = hir_vec![pat_arm, break_arm];
expr(lctx,
e.span,
hir::ExprMatch(next_expr, arms, hir::MatchSource::ForLoopDesugar),
None)
};
// `[opt_ident]: loop { ... }`
let loop_block = block_expr(lctx, match_expr);
let loop_expr = hir::ExprLoop(loop_block,
opt_ident.map(|ident| lower_ident(lctx, ident)));
let loop_expr = expr(lctx, e.span, loop_expr, None);
// `mut iter => { ... }`
let iter_arm = {
let iter_pat = pat_ident_binding_mode(lctx,
e.span,
iter,
hir::BindByValue(hir::MutMutable));
arm(hir_vec![iter_pat], loop_expr)
};
// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = {
let into_iter_path = {
let strs = std_path(lctx, &["iter", "IntoIterator", "into_iter"]);
path_global(e.span, strs)
};
// `::std::option::Option::None => break`
let break_arm = {
let break_expr = expr_break(lctx, e.span, None);
let into_iter = expr_path(lctx, into_iter_path, None);
expr_call(lctx, e.span, into_iter, hir_vec![head], None)
};
arm(hir_vec![pat_none(lctx, e.span)], break_expr)
};
// `match ::std::iter::Iterator::next(&mut iter) { ... }`
let match_expr = {
let next_path = {
let strs = std_path(lctx, &["iter", "Iterator", "next"]);
path_global(e.span, strs)
};
let iter = expr_ident(lctx, e.span, iter, None);
let ref_mut_iter = expr_mut_addr_of(lctx, e.span, iter, None);
let next_path = expr_path(lctx, next_path, None);
let next_expr = expr_call(lctx,
e.span,
next_path,
hir_vec![ref_mut_iter],
None);
let arms = hir_vec![pat_arm, break_arm];
expr(lctx,
e.span,
hir::ExprMatch(next_expr, arms, hir::MatchSource::ForLoopDesugar),
None)
};
// `[opt_ident]: loop { ... }`
let loop_block = block_expr(lctx, match_expr);
let loop_expr = hir::ExprLoop(loop_block,
opt_ident.map(|ident| lower_ident(lctx, ident)));
let loop_expr = expr(lctx, e.span, loop_expr, None);
// `mut iter => { ... }`
let iter_arm = {
let iter_pat = pat_ident_binding_mode(lctx,
e.span,
iter,
hir::BindByValue(hir::MutMutable));
arm(hir_vec![iter_pat], loop_expr)
};
// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = {
let into_iter_path = {
let strs = std_path(lctx, &["iter", "IntoIterator", "into_iter"]);
path_global(e.span, strs)
};
let into_iter = expr_path(lctx, into_iter_path, None);
expr_call(lctx, e.span, into_iter, hir_vec![head], None)
};
let match_expr = expr_match(lctx,
e.span,
into_iter_expr,
hir_vec![iter_arm],
hir::MatchSource::ForLoopDesugar,
None);
// `{ let _result = ...; _result }`
// underscore prevents an unused_variables lint if the head diverges
let result_ident = lctx.str_to_ident("_result");
let let_stmt = stmt_let(lctx,
let match_expr = expr_match(lctx,
e.span,
false,
result_ident,
match_expr,
into_iter_expr,
hir_vec![iter_arm],
hir::MatchSource::ForLoopDesugar,
None);
let result = expr_ident(lctx, e.span, result_ident, None);
let block = block_all(lctx, e.span, hir_vec![let_stmt], Some(result));
// add the attributes to the outer returned expr node
expr_block(lctx, block, e.attrs.clone())
});
// `{ let _result = ...; _result }`
// underscore prevents an unused_variables lint if the head diverges
let result_ident = lctx.str_to_ident("_result");
let let_stmt = stmt_let(lctx,
e.span,
false,
result_ident,
match_expr,
None);
let result = expr_ident(lctx, e.span, result_ident, None);
let block = block_all(lctx, e.span, hir_vec![let_stmt], Some(result));
// add the attributes to the outer returned expr node
return expr_block(lctx, block, e.attrs.clone());
}
// Desugar ExprKind::Try
@@ -1691,48 +1574,46 @@ fn make_struct(lctx: &LoweringContext,
// }
// }
return cache_ids(lctx, e.id, |lctx| {
// expand <expr>
let sub_expr = lower_expr(lctx, sub_expr);
// expand <expr>
let sub_expr = lower_expr(lctx, sub_expr);
// Ok(val) => val
let ok_arm = {
let val_ident = lctx.str_to_ident("val");
let val_pat = pat_ident(lctx, e.span, val_ident);
let val_expr = expr_ident(lctx, e.span, val_ident, None);
let ok_pat = pat_ok(lctx, e.span, val_pat);
// Ok(val) => val
let ok_arm = {
let val_ident = lctx.str_to_ident("val");
let val_pat = pat_ident(lctx, e.span, val_ident);
let val_expr = expr_ident(lctx, e.span, val_ident, None);
let ok_pat = pat_ok(lctx, e.span, val_pat);
arm(hir_vec![ok_pat], val_expr)
arm(hir_vec![ok_pat], val_expr)
};
// Err(err) => return Err(From::from(err))
let err_arm = {
let err_ident = lctx.str_to_ident("err");
let from_expr = {
let path = std_path(lctx, &["convert", "From", "from"]);
let path = path_global(e.span, path);
let from = expr_path(lctx, path, None);
let err_expr = expr_ident(lctx, e.span, err_ident, None);
expr_call(lctx, e.span, from, hir_vec![err_expr], None)
};
// Err(err) => return Err(From::from(err))
let err_arm = {
let err_ident = lctx.str_to_ident("err");
let from_expr = {
let path = std_path(lctx, &["convert", "From", "from"]);
let path = path_global(e.span, path);
let from = expr_path(lctx, path, None);
let err_expr = expr_ident(lctx, e.span, err_ident, None);
expr_call(lctx, e.span, from, hir_vec![err_expr], None)
};
let err_expr = {
let path = std_path(lctx, &["result", "Result", "Err"]);
let path = path_global(e.span, path);
let err_ctor = expr_path(lctx, path, None);
expr_call(lctx, e.span, err_ctor, hir_vec![from_expr], None)
};
let err_pat = pat_err(lctx, e.span,
pat_ident(lctx, e.span, err_ident));
let ret_expr = expr(lctx, e.span,
hir::Expr_::ExprRet(Some(err_expr)), None);
arm(hir_vec![err_pat], ret_expr)
let err_expr = {
let path = std_path(lctx, &["result", "Result", "Err"]);
let path = path_global(e.span, path);
let err_ctor = expr_path(lctx, path, None);
expr_call(lctx, e.span, err_ctor, hir_vec![from_expr], None)
};
let err_pat = pat_err(lctx, e.span,
pat_ident(lctx, e.span, err_ident));
let ret_expr = expr(lctx, e.span,
hir::Expr_::ExprRet(Some(err_expr)), None);
expr_match(lctx, e.span, sub_expr, hir_vec![err_arm, ok_arm],
hir::MatchSource::TryDesugar, None)
})
arm(hir_vec![err_pat], ret_expr)
};
return expr_match(lctx, e.span, sub_expr, hir_vec![err_arm, ok_arm],
hir::MatchSource::TryDesugar, None);
}
ExprKind::Mac(_) => panic!("Shouldn't exist here"),
@@ -2113,117 +1994,3 @@ fn signal_block_expr(lctx: &LoweringContext,
}),
attrs)
}
#[cfg(test)]
mod test {
use super::*;
use syntax::ast::{self, NodeId, NodeIdAssigner};
use syntax::{parse, codemap};
use syntax::fold::Folder;
use std::cell::Cell;
struct MockAssigner {
next_id: Cell<NodeId>,
}
impl MockAssigner {
fn new() -> MockAssigner {
MockAssigner { next_id: Cell::new(0) }
}
}
trait FakeExtCtxt {
fn call_site(&self) -> codemap::Span;
fn cfg(&self) -> ast::CrateConfig;
fn ident_of(&self, st: &str) -> ast::Ident;
fn name_of(&self, st: &str) -> ast::Name;
fn parse_sess(&self) -> &parse::ParseSess;
}
impl FakeExtCtxt for parse::ParseSess {
fn call_site(&self) -> codemap::Span {
codemap::Span {
lo: codemap::BytePos(0),
hi: codemap::BytePos(0),
expn_id: codemap::NO_EXPANSION,
}
}
fn cfg(&self) -> ast::CrateConfig {
Vec::new()
}
fn ident_of(&self, st: &str) -> ast::Ident {
parse::token::str_to_ident(st)
}
fn name_of(&self, st: &str) -> ast::Name {
parse::token::intern(st)
}
fn parse_sess(&self) -> &parse::ParseSess {
self
}
}
impl NodeIdAssigner for MockAssigner {
fn next_node_id(&self) -> NodeId {
let result = self.next_id.get();
self.next_id.set(result + 1);
result
}
fn peek_node_id(&self) -> NodeId {
self.next_id.get()
}
}
impl Folder for MockAssigner {
fn new_id(&mut self, old_id: NodeId) -> NodeId {
assert_eq!(old_id, ast::DUMMY_NODE_ID);
self.next_node_id()
}
}
#[test]
fn test_preserves_ids() {
let cx = parse::ParseSess::new();
let mut assigner = MockAssigner::new();
let ast_if_let = quote_expr!(&cx,
if let Some(foo) = baz {
bar(foo);
});
let ast_if_let = assigner.fold_expr(ast_if_let);
let ast_while_let = quote_expr!(&cx,
while let Some(foo) = baz {
bar(foo);
});
let ast_while_let = assigner.fold_expr(ast_while_let);
let ast_for = quote_expr!(&cx,
for i in 0..10 {
for j in 0..10 {
foo(i, j);
}
});
let ast_for = assigner.fold_expr(ast_for);
let ast_in = quote_expr!(&cx, in HEAP { foo() });
let ast_in = assigner.fold_expr(ast_in);
let lctx = LoweringContext::testing_context(&assigner);
let hir1 = lower_expr(&lctx, &ast_if_let);
let hir2 = lower_expr(&lctx, &ast_if_let);
assert!(hir1 == hir2);
let hir1 = lower_expr(&lctx, &ast_while_let);
let hir2 = lower_expr(&lctx, &ast_while_let);
assert!(hir1 == hir2);
let hir1 = lower_expr(&lctx, &ast_for);
let hir2 = lower_expr(&lctx, &ast_for);
assert!(hir1 == hir2);
let hir1 = lower_expr(&lctx, &ast_in);
let hir2 = lower_expr(&lctx, &ast_in);
assert!(hir1 == hir2);
}
}
+3 -13
View File
@@ -165,8 +165,7 @@ macro_rules! controller_entry_point {
&hir_map,
&expanded_crate,
&hir_map.krate(),
&id[..],
&lcx),
&id[..]),
Ok(()));
}
@@ -203,7 +202,6 @@ macro_rules! controller_entry_point {
&analysis,
mir_map.as_ref(),
tcx,
&lcx,
&id);
(control.after_analysis.callback)(state);
@@ -248,9 +246,7 @@ macro_rules! controller_entry_point {
}
fn keep_mtwt_tables(sess: &Session) -> bool {
sess.opts.debugging_opts.keep_mtwt_tables ||
sess.opts.debugging_opts.save_analysis ||
sess.opts.debugging_opts.save_analysis_csv
sess.opts.debugging_opts.keep_mtwt_tables
}
fn keep_ast(sess: &Session) -> bool {
@@ -345,7 +341,6 @@ pub struct CompileState<'a, 'ast: 'a, 'tcx: 'a> {
pub mir_map: Option<&'a MirMap<'tcx>>,
pub analysis: Option<&'a ty::CrateAnalysis<'a>>,
pub tcx: Option<&'a TyCtxt<'tcx>>,
pub lcx: Option<&'a LoweringContext<'a>>,
pub trans: Option<&'a trans::CrateTranslation>,
}
@@ -368,7 +363,6 @@ fn empty(input: &'a Input,
analysis: None,
mir_map: None,
tcx: None,
lcx: None,
trans: None,
}
}
@@ -400,15 +394,13 @@ fn state_after_write_deps(input: &'a Input,
hir_map: &'a hir_map::Map<'ast>,
krate: &'a ast::Crate,
hir_crate: &'a hir::Crate,
crate_name: &'a str,
lcx: &'a LoweringContext<'a>)
crate_name: &'a str)
-> CompileState<'a, 'ast, 'tcx> {
CompileState {
crate_name: Some(crate_name),
ast_map: Some(hir_map),
krate: Some(krate),
hir_crate: Some(hir_crate),
lcx: Some(lcx),
..CompileState::empty(input, session, out_dir)
}
}
@@ -421,7 +413,6 @@ fn state_after_analysis(input: &'a Input,
analysis: &'a ty::CrateAnalysis,
mir_map: Option<&'a MirMap<'tcx>>,
tcx: &'a TyCtxt<'tcx>,
lcx: &'a LoweringContext<'a>,
crate_name: &'a str)
-> CompileState<'a, 'ast, 'tcx> {
CompileState {
@@ -430,7 +421,6 @@ fn state_after_analysis(input: &'a Input,
tcx: Some(tcx),
krate: krate,
hir_crate: Some(hir_crate),
lcx: Some(lcx),
crate_name: Some(crate_name),
..CompileState::empty(input, session, out_dir)
}
-1
View File
@@ -499,7 +499,6 @@ fn build_controller(&mut self, sess: &Session) -> CompileController<'a> {
control.after_analysis.callback = box |state| {
time(state.session.time_passes(), "save analysis", || {
save::process_crate(state.tcx.unwrap(),
state.lcx.unwrap(),
state.krate.unwrap(),
state.analysis.unwrap(),
state.crate_name.unwrap(),
+2 -4
View File
@@ -42,8 +42,6 @@
use syntax::print::pprust::{path_to_string, ty_to_string};
use syntax::ptr::P;
use rustc::hir::lowering::lower_expr;
use super::{escape, generated_code, SaveContext, PathCollector};
use super::data::*;
use super::dump::Dump;
@@ -1222,7 +1220,7 @@ fn visit_expr(&mut self, ex: &ast::Expr) {
visit::walk_expr(self, ex);
}
ast::ExprKind::Struct(ref path, ref fields, ref base) => {
let hir_expr = lower_expr(self.save_ctxt.lcx, ex);
let hir_expr = self.save_ctxt.tcx.map.expect_expr(ex.id);
let adt = self.tcx.expr_ty(&hir_expr).ty_adt_def().unwrap();
let def = self.tcx.resolve_expr(&hir_expr);
self.process_struct_lit(ex, path, fields, adt.variant_of_def(def), base)
@@ -1241,7 +1239,7 @@ fn visit_expr(&mut self, ex: &ast::Expr) {
ast::ExprKind::TupField(ref sub_ex, idx) => {
self.visit_expr(&sub_ex);
let hir_node = lower_expr(self.save_ctxt.lcx, sub_ex);
let hir_node = self.save_ctxt.tcx.map.expect_expr(sub_ex.id);
let ty = &self.tcx.expr_ty_adjusted(&hir_node).sty;
match *ty {
ty::TyStruct(def, _) => {
+7 -13
View File
@@ -28,7 +28,7 @@
#[macro_use] extern crate syntax;
extern crate serialize as rustc_serialize;
use rustc::hir::{self, lowering};
use rustc::hir;
use rustc::hir::map::NodeItem;
use rustc::hir::def::Def;
use rustc::hir::def_id::DefId;
@@ -75,7 +75,6 @@ pub enum Row {
pub struct SaveContext<'l, 'tcx: 'l> {
tcx: &'l TyCtxt<'tcx>,
lcx: &'l lowering::LoweringContext<'l>,
span_utils: SpanUtils<'tcx>,
}
@@ -84,20 +83,16 @@ macro_rules! option_try(
);
impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
pub fn new(tcx: &'l TyCtxt<'tcx>,
lcx: &'l lowering::LoweringContext<'l>)
-> SaveContext<'l, 'tcx> {
pub fn new(tcx: &'l TyCtxt<'tcx>) -> SaveContext<'l, 'tcx> {
let span_utils = SpanUtils::new(&tcx.sess);
SaveContext::from_span_utils(tcx, lcx, span_utils)
SaveContext::from_span_utils(tcx, span_utils)
}
pub fn from_span_utils(tcx: &'l TyCtxt<'tcx>,
lcx: &'l lowering::LoweringContext<'l>,
span_utils: SpanUtils<'tcx>)
-> SaveContext<'l, 'tcx> {
SaveContext {
tcx: tcx,
lcx: lcx,
span_utils: span_utils,
}
}
@@ -378,14 +373,14 @@ pub fn get_trait_ref_data(&self,
}
pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
let hir_node = lowering::lower_expr(self.lcx, expr);
let hir_node = self.tcx.map.expect_expr(expr.id);
let ty = self.tcx.expr_ty_adjusted_opt(&hir_node);
if ty.is_none() || ty.unwrap().sty == ty::TyError {
return None;
}
match expr.node {
ast::ExprKind::Field(ref sub_ex, ident) => {
let hir_node = lowering::lower_expr(self.lcx, sub_ex);
let hir_node = self.tcx.map.expect_expr(sub_ex.id);
match self.tcx.expr_ty_adjusted(&hir_node).sty {
ty::TyStruct(def, _) => {
let f = def.struct_variant().field_named(ident.node.name);
@@ -405,7 +400,7 @@ pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
}
}
ast::ExprKind::Struct(ref path, _, _) => {
let hir_node = lowering::lower_expr(self.lcx, expr);
let hir_node = self.tcx.map.expect_expr(expr.id);
match self.tcx.expr_ty_adjusted(&hir_node).sty {
ty::TyStruct(def, _) => {
let sub_span = self.span_utils.span_for_last_ident(path.span);
@@ -704,7 +699,6 @@ fn extension(&self) -> &'static str {
}
pub fn process_crate<'l, 'tcx>(tcx: &'l TyCtxt<'tcx>,
lcx: &'l lowering::LoweringContext<'l>,
krate: &ast::Crate,
analysis: &'l ty::CrateAnalysis<'l>,
cratename: &str,
@@ -755,7 +749,7 @@ pub fn process_crate<'l, 'tcx>(tcx: &'l TyCtxt<'tcx>,
let output = &mut output_file;
let utils: SpanUtils<'tcx> = SpanUtils::new(&tcx.sess);
let save_ctxt = SaveContext::new(tcx, lcx);
let save_ctxt = SaveContext::new(tcx);
macro_rules! dump {
($new_dumper: expr) => {{