// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. // Lowers the AST to the HIR. // // Since the AST and HIR are fairly similar, this is mostly a simple procedure, // much like a fold. Where lowering involves a bit more work things get more // interesting and there are some invariants you should know about. These mostly // concern spans and ids. // // Spans are assigned to AST nodes during parsing and then are modified during // expansion to indicate the origin of a node and the process it went through // being expanded. Ids are assigned to AST nodes just before lowering. // // For the simpler lowering steps, ids and spans should be preserved. Unlike // expansion we do not preserve the process of lowering in the spans, so spans // should not be modified here. When creating a new node (as opposed to // 'folding' an existing one), then you create a new id using `next_id()`. // // You must ensure that ids are unique. That means that you should only use the // id from an AST node in a single HIR node (you can assume that AST node ids // 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 // HIR node can only have a single span. But multiple nodes can have the same // span and spans don't need to be kept in order, etc. Where code is preserved // by lowering, it should have the same span as in the AST. Where HIR nodes are // new it is probably best to give a span for the whole AST node being lowered. // All nodes should have real spans, don't use dummy spans. Tools are likely to // get confused if the spans from leaf AST nodes occur in multiple places // in the HIR, especially for multiple identifiers. use hir; use std::collections::BTreeMap; use std::collections::HashMap; use syntax::ast::*; use syntax::attr::{ThinAttributes, ThinAttributesExt}; use syntax::ext::mtwt; use syntax::ptr::P; use syntax::codemap::{respan, Spanned, Span}; use syntax::parse::token; use syntax::std_inject; use syntax::visit::{self, Visitor}; use std::cell::{Cell, RefCell}; pub struct LoweringContext<'a> { crate_root: Option<&'static str>, // Map AST ids to ids used for expanded nodes. id_cache: RefCell>, // Use if there are no cached ids for the current node. id_assigner: &'a NodeIdAssigner, // 0 == no cached id. Must be incremented to align with previous id // incrementing. cached_id: Cell, // Keep track of gensym'ed idents. gensym_cache: RefCell>, // A copy of cached_id, but is also set to an id while it is being cached. gensym_key: Cell, } impl<'a, 'hir> LoweringContext<'a> { pub fn new(id_assigner: &'a NodeIdAssigner, c: Option<&Crate>) -> LoweringContext<'a> { let crate_root = c.and_then(|c| { if std_inject::no_core(c) { None } else if std_inject::no_std(c) { Some("core") } else { Some("std") } }); 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), } } fn next_id(&self) -> NodeId { let cached = self.cached_id.get(); if cached == 0 { return self.id_assigner.next_node_id(); } self.cached_id.set(cached + 1); cached } fn str_to_ident(&self, s: &'static str) -> hir::Ident { let cached_id = self.gensym_key.get(); if cached_id == 0 { return hir::Ident::from_name(token::gensym(s)); } let cached = self.gensym_cache.borrow().contains_key(&(cached_id, s)); if cached { self.gensym_cache.borrow()[&(cached_id, s)] } else { let result = hir::Ident::from_name(token::gensym(s)); self.gensym_cache.borrow_mut().insert((cached_id, s), result); result } } } pub fn lower_ident(_lctx: &LoweringContext, ident: Ident) -> hir::Ident { hir::Ident { name: mtwt::resolve(ident), unhygienic_name: ident.name, } } pub fn lower_attrs(_lctx: &LoweringContext, attrs: &Vec) -> hir::HirVec { attrs.clone().into() } pub fn lower_view_path(lctx: &LoweringContext, view_path: &ViewPath) -> P { P(Spanned { node: match view_path.node { ViewPathSimple(ident, ref path) => { hir::ViewPathSimple(ident.name, lower_path(lctx, path)) } ViewPathGlob(ref path) => { hir::ViewPathGlob(lower_path(lctx, path)) } ViewPathList(ref path, ref path_list_idents) => { hir::ViewPathList(lower_path(lctx, path), path_list_idents.iter() .map(|path_list_ident| { Spanned { node: match path_list_ident.node { PathListIdent { id, name, rename } => hir::PathListIdent { id: id, name: name.name, rename: rename.map(|x| x.name), }, PathListMod { id, rename } => hir::PathListMod { id: id, rename: rename.map(|x| x.name), }, }, span: path_list_ident.span, } }) .collect()) } }, span: view_path.span, }) } pub fn lower_arm(lctx: &LoweringContext, arm: &Arm) -> hir::Arm { hir::Arm { attrs: lower_attrs(lctx, &arm.attrs), pats: arm.pats.iter().map(|x| lower_pat(lctx, x)).collect(), guard: arm.guard.as_ref().map(|ref x| lower_expr(lctx, x)), body: lower_expr(lctx, &arm.body), } } pub fn lower_decl(lctx: &LoweringContext, d: &Decl) -> P { match d.node { DeclLocal(ref l) => P(Spanned { node: hir::DeclLocal(lower_local(lctx, l)), span: d.span, }), DeclItem(ref it) => P(Spanned { node: hir::DeclItem(lower_item_id(lctx, it)), span: d.span, }), } } pub fn lower_ty_binding(lctx: &LoweringContext, b: &TypeBinding) -> hir::TypeBinding { hir::TypeBinding { id: b.id, name: b.ident.name, ty: lower_ty(lctx, &b.ty), span: b.span, } } pub fn lower_ty(lctx: &LoweringContext, t: &Ty) -> P { P(hir::Ty { id: t.id, node: match t.node { TyInfer => hir::TyInfer, TyVec(ref ty) => hir::TyVec(lower_ty(lctx, ty)), TyPtr(ref mt) => hir::TyPtr(lower_mt(lctx, mt)), TyRptr(ref region, ref mt) => { hir::TyRptr(lower_opt_lifetime(lctx, region), lower_mt(lctx, mt)) } TyBareFn(ref f) => { hir::TyBareFn(P(hir::BareFnTy { lifetimes: lower_lifetime_defs(lctx, &f.lifetimes), unsafety: lower_unsafety(lctx, f.unsafety), abi: f.abi, decl: lower_fn_decl(lctx, &f.decl), })) } TyTup(ref tys) => hir::TyTup(tys.iter().map(|ty| lower_ty(lctx, ty)).collect()), TyParen(ref ty) => { return lower_ty(lctx, ty); } TyPath(ref qself, ref path) => { let qself = qself.as_ref().map(|&QSelf { ref ty, position }| { hir::QSelf { ty: lower_ty(lctx, ty), position: position, } }); hir::TyPath(qself, lower_path(lctx, path)) } TyObjectSum(ref ty, ref bounds) => { hir::TyObjectSum(lower_ty(lctx, ty), lower_bounds(lctx, bounds)) } TyFixedLengthVec(ref ty, ref e) => { hir::TyFixedLengthVec(lower_ty(lctx, ty), lower_expr(lctx, e)) } TyTypeof(ref expr) => { hir::TyTypeof(lower_expr(lctx, expr)) } TyPolyTraitRef(ref bounds) => { hir::TyPolyTraitRef(bounds.iter().map(|b| lower_ty_param_bound(lctx, b)).collect()) } TyMac(_) => panic!("TyMac should have been expanded by now."), }, span: t.span, }) } pub fn lower_foreign_mod(lctx: &LoweringContext, fm: &ForeignMod) -> hir::ForeignMod { hir::ForeignMod { abi: fm.abi, items: fm.items.iter().map(|x| lower_foreign_item(lctx, x)).collect(), } } pub fn lower_variant(lctx: &LoweringContext, v: &Variant) -> hir::Variant { Spanned { node: hir::Variant_ { name: v.node.name.name, attrs: lower_attrs(lctx, &v.node.attrs), data: lower_variant_data(lctx, &v.node.data), disr_expr: v.node.disr_expr.as_ref().map(|e| lower_expr(lctx, e)), }, span: v.span, } } // Path segments are usually unhygienic, hygienic path segments can occur only in // identifier-like paths originating from `ExprPath`. // Make life simpler for rustc_resolve by renaming only such segments. pub fn lower_path_full(lctx: &LoweringContext, p: &Path, maybe_hygienic: bool) -> hir::Path { let maybe_hygienic = maybe_hygienic && !p.global && p.segments.len() == 1; hir::Path { global: p.global, segments: p.segments .iter() .map(|&PathSegment { identifier, ref parameters }| { hir::PathSegment { identifier: if maybe_hygienic { lower_ident(lctx, identifier) } else { hir::Ident::from_name(identifier.name) }, parameters: lower_path_parameters(lctx, parameters), } }) .collect(), span: p.span, } } pub fn lower_path(lctx: &LoweringContext, p: &Path) -> hir::Path { lower_path_full(lctx, p, false) } pub fn lower_path_parameters(lctx: &LoweringContext, path_parameters: &PathParameters) -> hir::PathParameters { match *path_parameters { PathParameters::AngleBracketed(ref data) => hir::AngleBracketedParameters(lower_angle_bracketed_parameter_data(lctx, data)), PathParameters::Parenthesized(ref data) => hir::ParenthesizedParameters(lower_parenthesized_parameter_data(lctx, data)), } } pub fn lower_angle_bracketed_parameter_data(lctx: &LoweringContext, data: &AngleBracketedParameterData) -> hir::AngleBracketedParameterData { let &AngleBracketedParameterData { ref lifetimes, ref types, ref bindings } = data; hir::AngleBracketedParameterData { lifetimes: lower_lifetimes(lctx, lifetimes), types: types.iter().map(|ty| lower_ty(lctx, ty)).collect(), bindings: bindings.iter().map(|b| lower_ty_binding(lctx, b)).collect(), } } pub fn lower_parenthesized_parameter_data(lctx: &LoweringContext, data: &ParenthesizedParameterData) -> hir::ParenthesizedParameterData { let &ParenthesizedParameterData { ref inputs, ref output, span } = data; hir::ParenthesizedParameterData { inputs: inputs.iter().map(|ty| lower_ty(lctx, ty)).collect(), output: output.as_ref().map(|ty| lower_ty(lctx, ty)), span: span, } } pub fn lower_local(lctx: &LoweringContext, l: &Local) -> P { P(hir::Local { id: l.id, ty: l.ty.as_ref().map(|t| lower_ty(lctx, t)), pat: lower_pat(lctx, &l.pat), init: l.init.as_ref().map(|e| lower_expr(lctx, e)), span: l.span, attrs: l.attrs.clone(), }) } pub fn lower_explicit_self_underscore(lctx: &LoweringContext, es: &ExplicitSelf_) -> hir::ExplicitSelf_ { match *es { SelfStatic => hir::SelfStatic, SelfValue(v) => hir::SelfValue(v.name), SelfRegion(ref lifetime, m, ident) => { hir::SelfRegion(lower_opt_lifetime(lctx, lifetime), lower_mutability(lctx, m), ident.name) } SelfExplicit(ref typ, ident) => { hir::SelfExplicit(lower_ty(lctx, typ), ident.name) } } } pub fn lower_mutability(_lctx: &LoweringContext, m: Mutability) -> hir::Mutability { match m { MutMutable => hir::MutMutable, MutImmutable => hir::MutImmutable, } } pub fn lower_explicit_self(lctx: &LoweringContext, s: &ExplicitSelf) -> hir::ExplicitSelf { Spanned { node: lower_explicit_self_underscore(lctx, &s.node), span: s.span, } } pub fn lower_arg(lctx: &LoweringContext, arg: &Arg) -> hir::Arg { hir::Arg { id: arg.id, pat: lower_pat(lctx, &arg.pat), ty: lower_ty(lctx, &arg.ty), } } pub fn lower_fn_decl(lctx: &LoweringContext, decl: &FnDecl) -> P { P(hir::FnDecl { inputs: decl.inputs.iter().map(|x| lower_arg(lctx, x)).collect(), output: match decl.output { Return(ref ty) => hir::Return(lower_ty(lctx, ty)), DefaultReturn(span) => hir::DefaultReturn(span), NoReturn(span) => hir::NoReturn(span), }, variadic: decl.variadic, }) } pub fn lower_ty_param_bound(lctx: &LoweringContext, tpb: &TyParamBound) -> hir::TyParamBound { match *tpb { TraitTyParamBound(ref ty, modifier) => { hir::TraitTyParamBound(lower_poly_trait_ref(lctx, ty), lower_trait_bound_modifier(lctx, modifier)) } RegionTyParamBound(ref lifetime) => { hir::RegionTyParamBound(lower_lifetime(lctx, lifetime)) } } } pub fn lower_ty_param(lctx: &LoweringContext, tp: &TyParam) -> hir::TyParam { hir::TyParam { id: tp.id, name: tp.ident.name, bounds: lower_bounds(lctx, &tp.bounds), default: tp.default.as_ref().map(|x| lower_ty(lctx, x)), span: tp.span, } } pub fn lower_ty_params(lctx: &LoweringContext, tps: &P<[TyParam]>) -> hir::HirVec { tps.iter().map(|tp| lower_ty_param(lctx, tp)).collect() } pub fn lower_lifetime(_lctx: &LoweringContext, l: &Lifetime) -> hir::Lifetime { hir::Lifetime { id: l.id, name: l.name, span: l.span, } } pub fn lower_lifetime_def(lctx: &LoweringContext, l: &LifetimeDef) -> hir::LifetimeDef { hir::LifetimeDef { lifetime: lower_lifetime(lctx, &l.lifetime), bounds: lower_lifetimes(lctx, &l.bounds), } } pub fn lower_lifetimes(lctx: &LoweringContext, lts: &Vec) -> hir::HirVec { lts.iter().map(|l| lower_lifetime(lctx, l)).collect() } pub fn lower_lifetime_defs(lctx: &LoweringContext, lts: &Vec) -> hir::HirVec { lts.iter().map(|l| lower_lifetime_def(lctx, l)).collect() } pub fn lower_opt_lifetime(lctx: &LoweringContext, o_lt: &Option) -> Option { o_lt.as_ref().map(|lt| lower_lifetime(lctx, lt)) } pub fn lower_generics(lctx: &LoweringContext, g: &Generics) -> hir::Generics { hir::Generics { ty_params: lower_ty_params(lctx, &g.ty_params), lifetimes: lower_lifetime_defs(lctx, &g.lifetimes), where_clause: lower_where_clause(lctx, &g.where_clause), } } pub fn lower_where_clause(lctx: &LoweringContext, wc: &WhereClause) -> hir::WhereClause { hir::WhereClause { id: wc.id, predicates: wc.predicates .iter() .map(|predicate| lower_where_predicate(lctx, predicate)) .collect(), } } pub fn lower_where_predicate(lctx: &LoweringContext, pred: &WherePredicate) -> hir::WherePredicate { match *pred { WherePredicate::BoundPredicate(WhereBoundPredicate{ ref bound_lifetimes, ref bounded_ty, ref bounds, span}) => { hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { bound_lifetimes: lower_lifetime_defs(lctx, bound_lifetimes), bounded_ty: lower_ty(lctx, bounded_ty), bounds: bounds.iter().map(|x| lower_ty_param_bound(lctx, x)).collect(), span: span, }) } WherePredicate::RegionPredicate(WhereRegionPredicate{ ref lifetime, ref bounds, span}) => { hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { span: span, lifetime: lower_lifetime(lctx, lifetime), bounds: bounds.iter().map(|bound| lower_lifetime(lctx, bound)).collect(), }) } WherePredicate::EqPredicate(WhereEqPredicate{ id, ref path, ref ty, span}) => { hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { id: id, path: lower_path(lctx, path), ty: lower_ty(lctx, ty), span: span, }) } } } pub fn lower_variant_data(lctx: &LoweringContext, vdata: &VariantData) -> hir::VariantData { match *vdata { VariantData::Struct(ref fields, id) => { hir::VariantData::Struct(fields.iter() .map(|f| lower_struct_field(lctx, f)) .collect(), id) } VariantData::Tuple(ref fields, id) => { hir::VariantData::Tuple(fields.iter() .map(|f| lower_struct_field(lctx, f)) .collect(), id) } VariantData::Unit(id) => hir::VariantData::Unit(id), } } pub fn lower_trait_ref(lctx: &LoweringContext, p: &TraitRef) -> hir::TraitRef { hir::TraitRef { path: lower_path(lctx, &p.path), ref_id: p.ref_id, } } pub fn lower_poly_trait_ref(lctx: &LoweringContext, p: &PolyTraitRef) -> hir::PolyTraitRef { hir::PolyTraitRef { bound_lifetimes: lower_lifetime_defs(lctx, &p.bound_lifetimes), trait_ref: lower_trait_ref(lctx, &p.trait_ref), span: p.span, } } pub fn lower_struct_field(lctx: &LoweringContext, f: &StructField) -> hir::StructField { Spanned { node: hir::StructField_ { id: f.node.id, kind: lower_struct_field_kind(lctx, &f.node.kind), ty: lower_ty(lctx, &f.node.ty), attrs: lower_attrs(lctx, &f.node.attrs), }, span: f.span, } } pub fn lower_field(lctx: &LoweringContext, f: &Field) -> hir::Field { hir::Field { name: respan(f.ident.span, f.ident.node.name), expr: lower_expr(lctx, &f.expr), span: f.span, } } pub fn lower_mt(lctx: &LoweringContext, mt: &MutTy) -> hir::MutTy { hir::MutTy { ty: lower_ty(lctx, &mt.ty), mutbl: lower_mutability(lctx, mt.mutbl), } } pub fn lower_opt_bounds(lctx: &LoweringContext, b: &Option) -> Option { b.as_ref().map(|ref bounds| lower_bounds(lctx, bounds)) } fn lower_bounds(lctx: &LoweringContext, bounds: &TyParamBounds) -> hir::TyParamBounds { bounds.iter().map(|bound| lower_ty_param_bound(lctx, bound)).collect() } pub fn lower_block(lctx: &LoweringContext, b: &Block) -> P { P(hir::Block { id: b.id, stmts: b.stmts.iter().map(|s| lower_stmt(lctx, s)).collect(), expr: b.expr.as_ref().map(|ref x| lower_expr(lctx, x)), rules: lower_block_check_mode(lctx, &b.rules), span: b.span, }) } pub fn lower_item_underscore(lctx: &LoweringContext, i: &Item_) -> hir::Item_ { match *i { ItemExternCrate(string) => hir::ItemExternCrate(string), ItemUse(ref view_path) => { hir::ItemUse(lower_view_path(lctx, view_path)) } ItemStatic(ref t, m, ref e) => { hir::ItemStatic(lower_ty(lctx, t), lower_mutability(lctx, m), lower_expr(lctx, e)) } ItemConst(ref t, ref e) => { hir::ItemConst(lower_ty(lctx, t), lower_expr(lctx, e)) } ItemFn(ref decl, unsafety, constness, abi, ref generics, ref body) => { hir::ItemFn(lower_fn_decl(lctx, decl), lower_unsafety(lctx, unsafety), lower_constness(lctx, constness), abi, lower_generics(lctx, generics), lower_block(lctx, body)) } ItemMod(ref m) => hir::ItemMod(lower_mod(lctx, m)), ItemForeignMod(ref nm) => hir::ItemForeignMod(lower_foreign_mod(lctx, nm)), ItemTy(ref t, ref generics) => { hir::ItemTy(lower_ty(lctx, t), lower_generics(lctx, generics)) } ItemEnum(ref enum_definition, ref generics) => { hir::ItemEnum(hir::EnumDef { variants: enum_definition.variants .iter() .map(|x| lower_variant(lctx, x)) .collect(), }, lower_generics(lctx, generics)) } ItemStruct(ref struct_def, ref generics) => { let struct_def = lower_variant_data(lctx, struct_def); hir::ItemStruct(struct_def, lower_generics(lctx, generics)) } ItemDefaultImpl(unsafety, ref trait_ref) => { hir::ItemDefaultImpl(lower_unsafety(lctx, unsafety), lower_trait_ref(lctx, trait_ref)) } ItemImpl(unsafety, polarity, ref generics, ref ifce, ref ty, ref impl_items) => { let new_impl_items = impl_items.iter() .map(|item| lower_impl_item(lctx, item)) .collect(); let ifce = ifce.as_ref().map(|trait_ref| lower_trait_ref(lctx, trait_ref)); hir::ItemImpl(lower_unsafety(lctx, unsafety), lower_impl_polarity(lctx, polarity), lower_generics(lctx, generics), ifce, lower_ty(lctx, ty), new_impl_items) } ItemTrait(unsafety, ref generics, ref bounds, ref items) => { let bounds = lower_bounds(lctx, bounds); let items = items.iter().map(|item| lower_trait_item(lctx, item)).collect(); hir::ItemTrait(lower_unsafety(lctx, unsafety), lower_generics(lctx, generics), bounds, items) } ItemMac(_) => panic!("Shouldn't still be around"), } } pub fn lower_trait_item(lctx: &LoweringContext, i: &TraitItem) -> hir::TraitItem { hir::TraitItem { id: i.id, name: i.ident.name, attrs: lower_attrs(lctx, &i.attrs), node: match i.node { ConstTraitItem(ref ty, ref default) => { hir::ConstTraitItem(lower_ty(lctx, ty), default.as_ref().map(|x| lower_expr(lctx, x))) } MethodTraitItem(ref sig, ref body) => { hir::MethodTraitItem(lower_method_sig(lctx, sig), body.as_ref().map(|x| lower_block(lctx, x))) } TypeTraitItem(ref bounds, ref default) => { hir::TypeTraitItem(lower_bounds(lctx, bounds), default.as_ref().map(|x| lower_ty(lctx, x))) } }, span: i.span, } } pub fn lower_impl_item(lctx: &LoweringContext, i: &ImplItem) -> hir::ImplItem { hir::ImplItem { id: i.id, name: i.ident.name, attrs: lower_attrs(lctx, &i.attrs), vis: lower_visibility(lctx, i.vis), node: match i.node { ImplItemKind::Const(ref ty, ref expr) => { hir::ImplItemKind::Const(lower_ty(lctx, ty), lower_expr(lctx, expr)) } ImplItemKind::Method(ref sig, ref body) => { hir::ImplItemKind::Method(lower_method_sig(lctx, sig), lower_block(lctx, body)) } ImplItemKind::Type(ref ty) => hir::ImplItemKind::Type(lower_ty(lctx, ty)), ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"), }, span: i.span, } } pub fn lower_mod(lctx: &LoweringContext, m: &Mod) -> hir::Mod { hir::Mod { inner: m.inner, item_ids: m.items.iter().map(|x| lower_item_id(lctx, x)).collect(), } } struct ItemLowerer<'lcx, 'interner: 'lcx> { items: BTreeMap, lctx: &'lcx LoweringContext<'interner>, } impl<'lcx, 'interner> Visitor<'lcx> for ItemLowerer<'lcx, 'interner> { fn visit_item(&mut self, item: &'lcx Item) { self.items.insert(item.id, lower_item(self.lctx, item)); visit::walk_item(self, item); } } pub fn lower_crate(lctx: &LoweringContext, c: &Crate) -> hir::Crate { let items = { let mut item_lowerer = ItemLowerer { items: BTreeMap::new(), lctx: lctx }; visit::walk_crate(&mut item_lowerer, c); item_lowerer.items }; hir::Crate { module: lower_mod(lctx, &c.module), attrs: lower_attrs(lctx, &c.attrs), config: c.config.clone().into(), span: c.span, exported_macros: c.exported_macros.iter().map(|m| lower_macro_def(lctx, m)).collect(), items: items, } } pub fn lower_macro_def(lctx: &LoweringContext, m: &MacroDef) -> hir::MacroDef { hir::MacroDef { name: m.ident.name, attrs: lower_attrs(lctx, &m.attrs), id: m.id, span: m.span, imported_from: m.imported_from.map(|x| x.name), export: m.export, use_locally: m.use_locally, allow_internal_unstable: m.allow_internal_unstable, body: m.body.clone().into(), } } pub fn lower_item_id(_lctx: &LoweringContext, i: &Item) -> hir::ItemId { hir::ItemId { id: i.id } } pub fn lower_item(lctx: &LoweringContext, i: &Item) -> hir::Item { let node = lower_item_underscore(lctx, &i.node); hir::Item { id: i.id, name: i.ident.name, attrs: lower_attrs(lctx, &i.attrs), node: node, vis: lower_visibility(lctx, i.vis), span: i.span, } } pub fn lower_foreign_item(lctx: &LoweringContext, i: &ForeignItem) -> hir::ForeignItem { hir::ForeignItem { id: i.id, name: i.ident.name, attrs: lower_attrs(lctx, &i.attrs), node: match i.node { ForeignItemFn(ref fdec, ref generics) => { hir::ForeignItemFn(lower_fn_decl(lctx, fdec), lower_generics(lctx, generics)) } ForeignItemStatic(ref t, m) => { hir::ForeignItemStatic(lower_ty(lctx, t), m) } }, vis: lower_visibility(lctx, i.vis), span: i.span, } } pub fn lower_method_sig(lctx: &LoweringContext, sig: &MethodSig) -> hir::MethodSig { hir::MethodSig { generics: lower_generics(lctx, &sig.generics), abi: sig.abi, explicit_self: lower_explicit_self(lctx, &sig.explicit_self), unsafety: lower_unsafety(lctx, sig.unsafety), constness: lower_constness(lctx, sig.constness), decl: lower_fn_decl(lctx, &sig.decl), } } pub fn lower_unsafety(_lctx: &LoweringContext, u: Unsafety) -> hir::Unsafety { match u { Unsafety::Unsafe => hir::Unsafety::Unsafe, Unsafety::Normal => hir::Unsafety::Normal, } } pub fn lower_constness(_lctx: &LoweringContext, c: Constness) -> hir::Constness { match c { Constness::Const => hir::Constness::Const, Constness::NotConst => hir::Constness::NotConst, } } pub fn lower_unop(_lctx: &LoweringContext, u: UnOp) -> hir::UnOp { match u { UnDeref => hir::UnDeref, UnNot => hir::UnNot, UnNeg => hir::UnNeg, } } pub fn lower_binop(_lctx: &LoweringContext, b: BinOp) -> hir::BinOp { Spanned { node: match b.node { BiAdd => hir::BiAdd, BiSub => hir::BiSub, BiMul => hir::BiMul, BiDiv => hir::BiDiv, BiRem => hir::BiRem, BiAnd => hir::BiAnd, BiOr => hir::BiOr, BiBitXor => hir::BiBitXor, BiBitAnd => hir::BiBitAnd, BiBitOr => hir::BiBitOr, BiShl => hir::BiShl, BiShr => hir::BiShr, BiEq => hir::BiEq, BiLt => hir::BiLt, BiLe => hir::BiLe, BiNe => hir::BiNe, BiGe => hir::BiGe, BiGt => hir::BiGt, }, span: b.span, } } pub fn lower_pat(lctx: &LoweringContext, p: &Pat) -> P { P(hir::Pat { id: p.id, node: match p.node { PatWild => hir::PatWild, PatIdent(ref binding_mode, pth1, ref sub) => { hir::PatIdent(lower_binding_mode(lctx, binding_mode), respan(pth1.span, lower_ident(lctx, pth1.node)), sub.as_ref().map(|x| lower_pat(lctx, x))) } PatLit(ref e) => hir::PatLit(lower_expr(lctx, e)), PatEnum(ref pth, ref pats) => { hir::PatEnum(lower_path(lctx, pth), pats.as_ref() .map(|pats| pats.iter().map(|x| lower_pat(lctx, x)).collect())) } PatQPath(ref qself, ref pth) => { let qself = hir::QSelf { ty: lower_ty(lctx, &qself.ty), position: qself.position, }; hir::PatQPath(qself, lower_path(lctx, pth)) } PatStruct(ref pth, ref fields, etc) => { let pth = lower_path(lctx, pth); let fs = fields.iter() .map(|f| { Spanned { span: f.span, node: hir::FieldPat { name: f.node.ident.name, pat: lower_pat(lctx, &f.node.pat), is_shorthand: f.node.is_shorthand, }, } }) .collect(); hir::PatStruct(pth, fs, etc) } PatTup(ref elts) => hir::PatTup(elts.iter().map(|x| lower_pat(lctx, x)).collect()), PatBox(ref inner) => hir::PatBox(lower_pat(lctx, inner)), PatRegion(ref inner, mutbl) => { hir::PatRegion(lower_pat(lctx, inner), lower_mutability(lctx, mutbl)) } PatRange(ref e1, ref e2) => { hir::PatRange(lower_expr(lctx, e1), lower_expr(lctx, e2)) } PatVec(ref before, ref slice, ref after) => { hir::PatVec(before.iter().map(|x| lower_pat(lctx, x)).collect(), slice.as_ref().map(|x| lower_pat(lctx, x)), after.iter().map(|x| lower_pat(lctx, x)).collect()) } PatMac(_) => panic!("Shouldn't exist here"), }, span: p.span, }) } // 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; { 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]); lctx.gensym_key.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"); } } else { let next_id = lctx.id_assigner.peek_node_id(); id_cache.insert(expr_id, next_id); lctx.gensym_key.set(next_id); } } let result = op(lctx); if reset_cached_id { lctx.cached_id.set(0); lctx.gensym_key.set(0); } result } pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { P(hir::Expr { id: e.id, node: match e.node { // Issue #22181: // Eventually a desugaring for `box EXPR` // (similar to the desugaring above for `in PLACE BLOCK`) // should go here, desugaring // // to: // // let mut place = BoxPlace::make_place(); // let raw_place = Place::pointer(&mut place); // let value = $value; // unsafe { // ::std::ptr::write(raw_place, value); // Boxed::finalize(place) // } // // But for now there are type-inference issues doing that. ExprBox(ref e) => { hir::ExprBox(lower_expr(lctx, e)) } // Desugar ExprBox: `in (PLACE) EXPR` ExprInPlace(ref placer, ref value_expr) => { // to: // // let p = PLACE; // let mut place = Placer::make_place(p); // let raw_place = Place::pointer(&mut place); // push_unsafe!({ // 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_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_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_mut = |lctx: &LoweringContext, bind, expr| { stmt_let(lctx, e.span, true, bind, expr, None) }; // let placer = ; 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 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) }; signal_block_expr(lctx, hir_vec![s1, s2, s3], expr, e.span, hir::PushUnstableBlock, e.attrs.clone()) }); } ExprVec(ref exprs) => { hir::ExprVec(exprs.iter().map(|x| lower_expr(lctx, x)).collect()) } ExprRepeat(ref expr, ref count) => { let expr = lower_expr(lctx, expr); let count = lower_expr(lctx, count); hir::ExprRepeat(expr, count) } ExprTup(ref elts) => { hir::ExprTup(elts.iter().map(|x| lower_expr(lctx, x)).collect()) } ExprCall(ref f, ref args) => { let f = lower_expr(lctx, f); hir::ExprCall(f, args.iter().map(|x| lower_expr(lctx, x)).collect()) } ExprMethodCall(i, ref tps, ref args) => { let tps = tps.iter().map(|x| lower_ty(lctx, x)).collect(); let args = args.iter().map(|x| lower_expr(lctx, x)).collect(); hir::ExprMethodCall(respan(i.span, i.node.name), tps, args) } ExprBinary(binop, ref lhs, ref rhs) => { let binop = lower_binop(lctx, binop); let lhs = lower_expr(lctx, lhs); let rhs = lower_expr(lctx, rhs); hir::ExprBinary(binop, lhs, rhs) } ExprUnary(op, ref ohs) => { let op = lower_unop(lctx, op); let ohs = lower_expr(lctx, ohs); hir::ExprUnary(op, ohs) } ExprLit(ref l) => hir::ExprLit(P((**l).clone())), ExprCast(ref expr, ref ty) => { let expr = lower_expr(lctx, expr); hir::ExprCast(expr, lower_ty(lctx, ty)) } ExprType(ref expr, ref ty) => { let expr = lower_expr(lctx, expr); hir::ExprType(expr, lower_ty(lctx, ty)) } ExprAddrOf(m, ref ohs) => { let m = lower_mutability(lctx, m); let ohs = lower_expr(lctx, ohs); hir::ExprAddrOf(m, ohs) } // More complicated than you might expect because the else branch // might be `if let`. ExprIf(ref cond, ref blk, ref else_opt) => { let else_opt = else_opt.as_ref().map(|els| { match els.node { ExprIfLet(..) => { 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) }) } _ => lower_expr(lctx, els), } }); hir::ExprIf(lower_expr(lctx, cond), lower_block(lctx, blk), else_opt) } ExprWhile(ref cond, ref body, opt_ident) => { hir::ExprWhile(lower_expr(lctx, cond), lower_block(lctx, body), opt_ident.map(|ident| lower_ident(lctx, ident))) } ExprLoop(ref body, opt_ident) => { hir::ExprLoop(lower_block(lctx, body), opt_ident.map(|ident| lower_ident(lctx, ident))) } ExprMatch(ref expr, ref arms) => { hir::ExprMatch(lower_expr(lctx, expr), arms.iter().map(|x| lower_arm(lctx, x)).collect(), hir::MatchSource::Normal) } ExprClosure(capture_clause, ref decl, ref body) => { hir::ExprClosure(lower_capture_clause(lctx, capture_clause), lower_fn_decl(lctx, decl), lower_block(lctx, body)) } ExprBlock(ref blk) => hir::ExprBlock(lower_block(lctx, blk)), ExprAssign(ref el, ref er) => { hir::ExprAssign(lower_expr(lctx, el), lower_expr(lctx, er)) } ExprAssignOp(op, ref el, ref er) => { hir::ExprAssignOp(lower_binop(lctx, op), lower_expr(lctx, el), lower_expr(lctx, er)) } ExprField(ref el, ident) => { hir::ExprField(lower_expr(lctx, el), respan(ident.span, ident.node.name)) } ExprTupField(ref el, ident) => { hir::ExprTupField(lower_expr(lctx, el), ident) } ExprIndex(ref el, ref er) => { hir::ExprIndex(lower_expr(lctx, el), lower_expr(lctx, er)) } ExprRange(ref e1, ref e2) => { hir::ExprRange(e1.as_ref().map(|x| lower_expr(lctx, x)), e2.as_ref().map(|x| lower_expr(lctx, x))) } ExprPath(ref qself, ref path) => { let hir_qself = qself.as_ref().map(|&QSelf { ref ty, position }| { hir::QSelf { ty: lower_ty(lctx, ty), position: position, } }); hir::ExprPath(hir_qself, lower_path_full(lctx, path, qself.is_none())) } ExprBreak(opt_ident) => hir::ExprBreak(opt_ident.map(|sp_ident| { respan(sp_ident.span, lower_ident(lctx, sp_ident.node)) })), ExprAgain(opt_ident) => hir::ExprAgain(opt_ident.map(|sp_ident| { respan(sp_ident.span, lower_ident(lctx, sp_ident.node)) })), ExprRet(ref e) => hir::ExprRet(e.as_ref().map(|x| lower_expr(lctx, x))), ExprInlineAsm(InlineAsm { ref inputs, ref outputs, ref asm, asm_str_style, ref clobbers, volatile, alignstack, dialect, expn_id, }) => hir::ExprInlineAsm(hir::InlineAsm { inputs: inputs.iter() .map(|&(ref c, ref input)| (c.clone(), lower_expr(lctx, input))) .collect(), outputs: outputs.iter() .map(|out| { hir::InlineAsmOutput { constraint: out.constraint.clone(), expr: lower_expr(lctx, &out.expr), is_rw: out.is_rw, is_indirect: out.is_indirect, } }) .collect(), asm: asm.clone(), asm_str_style: asm_str_style, clobbers: clobbers.clone().into(), volatile: volatile, alignstack: alignstack, dialect: dialect, expn_id: expn_id, }), ExprStruct(ref path, ref fields, ref maybe_expr) => { hir::ExprStruct(lower_path(lctx, path), fields.iter().map(|x| lower_field(lctx, x)).collect(), maybe_expr.as_ref().map(|x| lower_expr(lctx, x))) } ExprParen(ref ex) => { // merge attributes into the inner expression. return lower_expr(lctx, ex).map(|mut ex| { ex.attrs.update(|attrs| { attrs.prepend(e.attrs.clone()) }); ex }); } // Desugar ExprIfLet // From: `if let = []` ExprIfLet(ref pat, ref sub_expr, ref body, ref else_opt) => { // to: // // match { // => , // [_ if => ,] // _ => [ | ()] // } return cache_ids(lctx, e.id, |lctx| { // ` => ` 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 => ,]` 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)), } }) }); 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 }; let contains_else_clause = else_opt.is_some(); // `_ => [ | ()]` 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 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()) }); } // Desugar ExprWhileLet // From: `[opt_ident]: while let = ` ExprWhileLet(ref pat, ref sub_expr, ref body, opt_ident) => { // to: // // [opt_ident]: loop { // match { // => , // _ => break // } // } return cache_ids(lctx, e.id, |lctx| { // ` => ` 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) }; // `match { ... }` 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()) }); } // Desugar ExprForLoop // From: `[opt_ident]: for in ` ExprForLoop(ref pat, ref head, ref body, opt_ident) => { // to: // // { // let result = match ::std::iter::IntoIterator::into_iter() { // mut iter => { // [opt_ident]: loop { // match ::std::iter::Iterator::next(&mut iter) { // ::std::option::Option::Some() => , // ::std::option::Option::None => break // } // } // } // }; // result // } return cache_ids(lctx, e.id, |lctx| { // expand let head = lower_expr(lctx, head); let iter = lctx.str_to_ident("iter"); // `::std::option::Option::Some() => ` 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) }; // `::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() { ... }` 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, 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 expr_block(lctx, block, e.attrs.clone()) }); } ExprMac(_) => panic!("Shouldn't exist here"), }, span: e.span, attrs: e.attrs.clone(), }) } pub fn lower_stmt(lctx: &LoweringContext, s: &Stmt) -> hir::Stmt { match s.node { StmtDecl(ref d, id) => { Spanned { node: hir::StmtDecl(lower_decl(lctx, d), id), span: s.span, } } StmtExpr(ref e, id) => { Spanned { node: hir::StmtExpr(lower_expr(lctx, e), id), span: s.span, } } StmtSemi(ref e, id) => { Spanned { node: hir::StmtSemi(lower_expr(lctx, e), id), span: s.span, } } StmtMac(..) => panic!("Shouldn't exist here"), } } pub fn lower_capture_clause(_lctx: &LoweringContext, c: CaptureClause) -> hir::CaptureClause { match c { CaptureByValue => hir::CaptureByValue, CaptureByRef => hir::CaptureByRef, } } pub fn lower_visibility(_lctx: &LoweringContext, v: Visibility) -> hir::Visibility { match v { Public => hir::Public, Inherited => hir::Inherited, } } pub fn lower_block_check_mode(lctx: &LoweringContext, b: &BlockCheckMode) -> hir::BlockCheckMode { match *b { DefaultBlock => hir::DefaultBlock, UnsafeBlock(u) => hir::UnsafeBlock(lower_unsafe_source(lctx, u)), } } pub fn lower_binding_mode(lctx: &LoweringContext, b: &BindingMode) -> hir::BindingMode { match *b { BindingMode::ByRef(m) => hir::BindByRef(lower_mutability(lctx, m)), BindingMode::ByValue(m) => hir::BindByValue(lower_mutability(lctx, m)), } } pub fn lower_struct_field_kind(lctx: &LoweringContext, s: &StructFieldKind) -> hir::StructFieldKind { match *s { NamedField(ident, vis) => hir::NamedField(ident.name, lower_visibility(lctx, vis)), UnnamedField(vis) => hir::UnnamedField(lower_visibility(lctx, vis)), } } pub fn lower_unsafe_source(_lctx: &LoweringContext, u: UnsafeSource) -> hir::UnsafeSource { match u { CompilerGenerated => hir::CompilerGenerated, UserProvided => hir::UserProvided, } } pub fn lower_impl_polarity(_lctx: &LoweringContext, i: ImplPolarity) -> hir::ImplPolarity { match i { ImplPolarity::Positive => hir::ImplPolarity::Positive, ImplPolarity::Negative => hir::ImplPolarity::Negative, } } pub fn lower_trait_bound_modifier(_lctx: &LoweringContext, f: TraitBoundModifier) -> hir::TraitBoundModifier { match f { TraitBoundModifier::None => hir::TraitBoundModifier::None, TraitBoundModifier::Maybe => hir::TraitBoundModifier::Maybe, } } // Helper methods for building HIR. fn arm(pats: hir::HirVec>, expr: P) -> hir::Arm { hir::Arm { attrs: hir_vec![], pats: pats, guard: None, body: expr, } } fn expr_break(lctx: &LoweringContext, span: Span, attrs: ThinAttributes) -> P { expr(lctx, span, hir::ExprBreak(None), attrs) } fn expr_call(lctx: &LoweringContext, span: Span, e: P, args: hir::HirVec>, attrs: ThinAttributes) -> P { expr(lctx, span, hir::ExprCall(e, args), attrs) } fn expr_ident(lctx: &LoweringContext, span: Span, id: hir::Ident, attrs: ThinAttributes) -> P { expr_path(lctx, path_ident(span, id), attrs) } fn expr_mut_addr_of(lctx: &LoweringContext, span: Span, e: P, attrs: ThinAttributes) -> P { expr(lctx, span, hir::ExprAddrOf(hir::MutMutable, e), attrs) } fn expr_path(lctx: &LoweringContext, path: hir::Path, attrs: ThinAttributes) -> P { expr(lctx, path.span, hir::ExprPath(None, path), attrs) } fn expr_match(lctx: &LoweringContext, span: Span, arg: P, arms: hir::HirVec, source: hir::MatchSource, attrs: ThinAttributes) -> P { expr(lctx, span, hir::ExprMatch(arg, arms, source), attrs) } fn expr_block(lctx: &LoweringContext, b: P, attrs: ThinAttributes) -> P { expr(lctx, b.span, hir::ExprBlock(b), attrs) } fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: hir::HirVec>, attrs: ThinAttributes) -> P { expr(lctx, sp, hir::ExprTup(exprs), attrs) } fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_, attrs: ThinAttributes) -> P { P(hir::Expr { id: lctx.next_id(), node: node, span: span, attrs: attrs, }) } fn stmt_let(lctx: &LoweringContext, sp: Span, mutbl: bool, ident: hir::Ident, ex: P, attrs: ThinAttributes) -> hir::Stmt { let pat = if mutbl { pat_ident_binding_mode(lctx, sp, ident, hir::BindByValue(hir::MutMutable)) } else { pat_ident(lctx, sp, ident) }; let local = P(hir::Local { pat: pat, ty: None, init: Some(ex), id: lctx.next_id(), span: sp, attrs: attrs, }); let decl = respan(sp, hir::DeclLocal(local)); respan(sp, hir::StmtDecl(P(decl), lctx.next_id())) } fn block_expr(lctx: &LoweringContext, expr: P) -> P { block_all(lctx, expr.span, hir::HirVec::new(), Some(expr)) } fn block_all(lctx: &LoweringContext, span: Span, stmts: hir::HirVec, expr: Option>) -> P { P(hir::Block { stmts: stmts, expr: expr, id: lctx.next_id(), rules: hir::DefaultBlock, span: span, }) } fn pat_some(lctx: &LoweringContext, span: Span, pat: P) -> P { let some = std_path(lctx, &["option", "Option", "Some"]); let path = path_global(span, some); pat_enum(lctx, span, path, hir_vec![pat]) } fn pat_none(lctx: &LoweringContext, span: Span) -> P { let none = std_path(lctx, &["option", "Option", "None"]); let path = path_global(span, none); pat_enum(lctx, span, path, hir_vec![]) } fn pat_enum(lctx: &LoweringContext, span: Span, path: hir::Path, subpats: hir::HirVec>) -> P { let pt = hir::PatEnum(path, Some(subpats)); pat(lctx, span, pt) } fn pat_ident(lctx: &LoweringContext, span: Span, ident: hir::Ident) -> P { pat_ident_binding_mode(lctx, span, ident, hir::BindByValue(hir::MutImmutable)) } fn pat_ident_binding_mode(lctx: &LoweringContext, span: Span, ident: hir::Ident, bm: hir::BindingMode) -> P { let pat_ident = hir::PatIdent(bm, Spanned { span: span, node: ident, }, None); pat(lctx, span, pat_ident) } fn pat_wild(lctx: &LoweringContext, span: Span) -> P { pat(lctx, span, hir::PatWild) } fn pat(lctx: &LoweringContext, span: Span, pat: hir::Pat_) -> P { P(hir::Pat { id: lctx.next_id(), node: pat, span: span, }) } fn path_ident(span: Span, id: hir::Ident) -> hir::Path { path(span, vec![id]) } fn path(span: Span, strs: Vec) -> hir::Path { path_all(span, false, strs, hir::HirVec::new(), hir::HirVec::new(), hir::HirVec::new()) } fn path_global(span: Span, strs: Vec) -> hir::Path { path_all(span, true, strs, hir::HirVec::new(), hir::HirVec::new(), hir::HirVec::new()) } fn path_all(sp: Span, global: bool, mut idents: Vec, lifetimes: hir::HirVec, types: hir::HirVec>, bindings: hir::HirVec) -> hir::Path { let last_identifier = idents.pop().unwrap(); let mut segments: Vec = idents.into_iter() .map(|ident| { hir::PathSegment { identifier: ident, parameters: hir::PathParameters::none(), } }) .collect(); segments.push(hir::PathSegment { identifier: last_identifier, parameters: hir::AngleBracketedParameters(hir::AngleBracketedParameterData { lifetimes: lifetimes, types: types, bindings: bindings, }), }); hir::Path { span: sp, global: global, segments: segments.into(), } } fn std_path(lctx: &LoweringContext, components: &[&str]) -> Vec { let mut v = Vec::new(); if let Some(s) = lctx.crate_root { v.push(hir::Ident::from_name(token::intern(s))); } v.extend(components.iter().map(|s| hir::Ident::from_name(token::intern(s)))); return v; } // Given suffix ["b","c","d"], returns path `::std::b::c::d` when // `fld.cx.use_std`, and `::core::b::c::d` otherwise. fn core_path(lctx: &LoweringContext, span: Span, components: &[&str]) -> hir::Path { let idents = std_path(lctx, components); path_global(span, idents) } fn signal_block_expr(lctx: &LoweringContext, stmts: hir::HirVec, expr: P, span: Span, rule: hir::BlockCheckMode, attrs: ThinAttributes) -> P { let id = lctx.next_id(); expr_block(lctx, P(hir::Block { rules: rule, span: span, id: id, stmts: stmts, expr: Some(expr), }), 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, } 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 { foo(i); }); 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::new(&assigner, None); 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); } }