diff --git a/RELEASES.md b/RELEASES.md index 53139877c2f92..5afc6f9bdc0cb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,140 @@ +Version 1.40.0 (2019-12-19) +=========================== + +Language +-------- +- [You can now use tuple `struct`s and tuple `enum` variant's constructors in + `const` contexts.][65188] e.g. + + ```rust + pub struct Point(i32, i32); + + const ORIGIN: Point = { + let constructor = Point; + + constructor(0, 0) + }; + ``` + +- [You can now mark `struct`s, `enum`s, and `enum` variants with the `#[non_exhaustive]` attribute to + indicate that there may be variants or fields added in the future.][64639] + For example this requires adding a wild-card branch (`_ => {}`) to any match + statements on a non-exhaustive `enum`. [(RFC 2008)] +- [You can now use function-like procedural macros in `extern` blocks and in + type positions.][63931] e.g. `type Generated = macro!();` +- [Function-like and attribute procedural macros can now emit + `macro_rules!` items, so you can now have your macros generate macros.][64035] +- [The `meta` pattern matcher in `macro_rules!` now correctly matches the modern + attribute syntax.][63674] For example `(#[$m:meta])` now matches `#[attr]`, + `#[attr{tokens}]`, `#[attr[tokens]]`, and `#[attr(tokens)]`. + +Compiler +-------- +- [Added tier 3 support\* for the + `thumbv7neon-unknown-linux-musleabihf` target.][66103] +- [Added tier 3 support for the + `aarch64-unknown-none-softfloat` target.][64589] +- [Added tier 3 support for the `mips64-unknown-linux-muslabi64`, and + `mips64el-unknown-linux-muslabi64` targets.][65843] + +\* Refer to Rust's [platform support page][forge-platform-support] for more + information on Rust's tiered platform support. + +Libraries +--------- +- [The `is_power_of_two` method on unsigned numeric types is now a `const` function.][65092] + +Stabilized APIs +--------------- +- [`BTreeMap::get_key_value`] +- [`HashMap::get_key_value`] +- [`Option::as_deref_mut`] +- [`Option::as_deref`] +- [`Option::flatten`] +- [`UdpSocket::peer_addr`] +- [`f32::to_be_bytes`] +- [`f32::to_le_bytes`] +- [`f32::to_ne_bytes`] +- [`f64::to_be_bytes`] +- [`f64::to_le_bytes`] +- [`f64::to_ne_bytes`] +- [`f32::from_be_bytes`] +- [`f32::from_le_bytes`] +- [`f32::from_ne_bytes`] +- [`f64::from_be_bytes`] +- [`f64::from_le_bytes`] +- [`f64::from_ne_bytes`] +- [`mem::take`] +- [`slice::repeat`] +- [`todo!`] + +Cargo +----- +- [Cargo will now always display warnings, rather than only on + fresh builds.][cargo/7450] +- [Feature flags (except `--all-features`) passed to a virtual workspace will + now produce an error.][cargo/7507] Previously these flags were ignored. +- [You can now publish `dev-dependencies` without including + a `version`.][cargo/7333] + +Misc +---- +- [You can now specify the `#[cfg(doctest)]` attribute to include an item only + when running documentation tests with `rustdoc`.][63803] + +Compatibility Notes +------------------- +- [As previously announced, any previous NLL warnings in the 2015 edition are + now hard errors.][64221] +- [The `include!` macro will now warn if it failed to include the + entire file.][64284] The `include!` macro unintentionally only includes the + first _expression_ in a file, and this can be unintuitive. This will become + either a hard error in a future release, or the behavior may be fixed to include all expressions as expected. +- [Using `#[inline]` on function prototypes and consts now emits a warning under + `unused_attribute` lint.][65294] Using `#[inline]` anywhere else inside traits + or `extern` blocks now correctly emits a hard error. + +[65294]: https://github.com/rust-lang/rust/pull/65294/ +[66103]: https://github.com/rust-lang/rust/pull/66103/ +[65843]: https://github.com/rust-lang/rust/pull/65843/ +[65188]: https://github.com/rust-lang/rust/pull/65188/ +[65092]: https://github.com/rust-lang/rust/pull/65092/ +[64589]: https://github.com/rust-lang/rust/pull/64589/ +[64639]: https://github.com/rust-lang/rust/pull/64639/ +[64221]: https://github.com/rust-lang/rust/pull/64221/ +[64284]: https://github.com/rust-lang/rust/pull/64284/ +[63931]: https://github.com/rust-lang/rust/pull/63931/ +[64035]: https://github.com/rust-lang/rust/pull/64035/ +[63674]: https://github.com/rust-lang/rust/pull/63674/ +[63803]: https://github.com/rust-lang/rust/pull/63803/ +[cargo/7450]: https://github.com/rust-lang/cargo/pull/7450/ +[cargo/7507]: https://github.com/rust-lang/cargo/pull/7507/ +[cargo/7525]: https://github.com/rust-lang/cargo/pull/7525/ +[cargo/7333]: https://github.com/rust-lang/cargo/pull/7333/ +[(rfc 2008)]: https://rust-lang.github.io/rfcs/2008-non-exhaustive.html +[`f32::to_be_bytes`]: https://doc.rust-lang.org/std/primitive.f32.html#method.to_be_bytes +[`f32::to_le_bytes`]: https://doc.rust-lang.org/std/primitive.f32.html#method.to_le_bytes +[`f32::to_ne_bytes`]: https://doc.rust-lang.org/std/primitive.f32.html#method.to_ne_bytes +[`f64::to_be_bytes`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_be_bytes +[`f64::to_le_bytes`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_le_bytes +[`f64::to_ne_bytes`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_ne_bytes +[`f32::from_be_bytes`]: https://doc.rust-lang.org/std/primitive.f32.html#method.from_be_bytes +[`f32::from_le_bytes`]: https://doc.rust-lang.org/std/primitive.f32.html#method.from_le_bytes +[`f32::from_ne_bytes`]: https://doc.rust-lang.org/std/primitive.f32.html#method.from_ne_bytes +[`f64::from_be_bytes`]: https://doc.rust-lang.org/std/primitive.f64.html#method.from_be_bytes +[`f64::from_le_bytes`]: https://doc.rust-lang.org/std/primitive.f64.html#method.from_le_bytes +[`f64::from_ne_bytes`]: https://doc.rust-lang.org/std/primitive.f64.html#method.from_ne_bytes +[`option::flatten`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.flatten +[`option::as_deref`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.as_deref +[`option::as_deref_mut`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.as_deref_mut +[`hashmap::get_key_value`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.get_key_value +[`btreemap::get_key_value`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.get_key_value +[`slice::repeat`]: https://doc.rust-lang.org/std/primitive.slice.html#method.repeat +[`mem::take`]: https://doc.rust-lang.org/std/mem/fn.take.html +[`udpsocket::peer_addr`]: https://doc.rust-lang.org/std/net/struct.UdpSocket.html#method.peer_addr +[`todo!`]: https://doc.rust-lang.org/std/macro.todo.html + + Version 1.39.0 (2019-11-07) =========================== diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 1a700b990569c..6e165ccb91998 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -92,7 +92,7 @@ use crate::raw_vec::RawVec; /// vec[0] = 7; /// assert_eq!(vec[0], 7); /// -/// vec.extend([1, 2, 3].iter().cloned()); +/// vec.extend([1, 2, 3].iter().copied()); /// /// for x in &vec { /// println!("{}", x); diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index f37c57e38b540..4cfd6527deb0a 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -253,7 +253,7 @@ impl Layout { /// Creates a layout describing the record for `self` followed by /// `next`, including any necessary padding to ensure that `next` - /// will be properly aligned. Note that the result layout will + /// will be properly aligned. Note that the resulting layout will /// satisfy the alignment properties of both `self` and `next`. /// /// The resulting layout will be the same as that of a C struct containing @@ -387,7 +387,7 @@ impl fmt::Display for CannotReallocInPlace { } /// A memory allocator that can be registered as the standard library’s default -/// though the `#[global_allocator]` attributes. +/// through the `#[global_allocator]` attribute. /// /// Some of the methods require that a memory block be *currently /// allocated* via an allocator. This means that: @@ -458,7 +458,7 @@ pub unsafe trait GlobalAlloc { /// # Errors /// /// Returning a null pointer indicates that either memory is exhausted - /// or `layout` does not meet allocator's size or alignment constraints. + /// or `layout` does not meet this allocator's size or alignment constraints. /// /// Implementations are encouraged to return null on memory /// exhaustion rather than aborting, but this is not @@ -1045,7 +1045,7 @@ pub unsafe trait Alloc { /// Captures a common usage pattern for allocators. /// /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. + /// `realloc`/`dealloc` methods of this allocator. /// /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` /// must be considered "currently allocated" and must be @@ -1111,7 +1111,7 @@ pub unsafe trait Alloc { /// Captures a common usage pattern for allocators. /// /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. + /// `realloc`/`dealloc` methods of this allocator. /// /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` /// must be considered "currently allocated" and must be @@ -1158,7 +1158,7 @@ pub unsafe trait Alloc { /// Captures a common usage pattern for allocators. /// /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. + /// `realloc`/`dealloc` methods of this allocator. /// /// # Safety /// diff --git a/src/librustc_codegen_ssa/mir/constant.rs b/src/librustc_codegen_ssa/mir/constant.rs index 27891be6b82c5..fb8f504d04b10 100644 --- a/src/librustc_codegen_ssa/mir/constant.rs +++ b/src/librustc_codegen_ssa/mir/constant.rs @@ -16,6 +16,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { match constant.literal.val { + // Special case unevaluated statics, because statics have an identity and thus should + // use `get_static` to get at their id. + // FIXME(oli-obk): can we unify this somehow, maybe by making const eval of statics + // always produce `&STATIC`. This may also simplify how const eval works with statics. ty::ConstKind::Unevaluated(def_id, substs) if self.cx.tcx().is_static(def_id) => { assert!(substs.is_empty(), "we don't support generic statics yet"); @@ -46,7 +50,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { instance, promoted: None, }; - self.cx.tcx().const_eval(ty::ParamEnv::reveal_all().and(cid)) + self.cx.tcx().const_eval(ty::ParamEnv::reveal_all().and(cid)).map_err(|err| { + self.cx.tcx().sess.span_err(constant.span, "erroneous constant encountered"); + err + }) }, _ => Ok(self.monomorphize(&constant.literal)), } diff --git a/src/librustc_error_codes/error_codes/E0478.md b/src/librustc_error_codes/error_codes/E0478.md index 6634d9b6e63b4..4bc5fde2e897e 100644 --- a/src/librustc_error_codes/error_codes/E0478.md +++ b/src/librustc_error_codes/error_codes/E0478.md @@ -21,8 +21,8 @@ this issue, you need to specify it: ``` trait Wedding<'t>: 't { } -struct Prince<'kiss, 'SnowWhite: 'kiss> { // You say here that 'kiss must live - // longer than 'SnowWhite. +struct Prince<'kiss, 'SnowWhite: 'kiss> { // You say here that 'SnowWhite + // must live longer than 'kiss. child: Box + 'SnowWhite>, // And now it's all good! } ``` diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index cae303039a194..dbeccab966bbb 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -97,6 +97,7 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> { location: Location, ) { if let Some(all_facts) = self.all_facts { + let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); all_facts.cfg_edge.push(( self.location_table.start_index(location), self.location_table.mid_index(location), @@ -142,6 +143,7 @@ impl<'cg, 'cx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'tcx> { location: Location, ) { if let Some(all_facts) = self.all_facts { + let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); all_facts.cfg_edge.push(( self.location_table.start_index(location), self.location_table.mid_index(location), @@ -205,6 +207,8 @@ impl<'cx, 'cg, 'tcx> ConstraintGeneration<'cx, 'cg, 'tcx> { /// as `killed`. For example, when assigning to a local, or on a call's return destination. fn record_killed_borrows_for_place(&mut self, place: &Place<'tcx>, location: Location) { if let Some(all_facts) = self.all_facts { + let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); + // Depending on the `Place` we're killing: // - if it's a local, or a single deref of a local, // we kill all the borrows on the local. diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index 2442bdf8a9b4e..e442f9c9e322f 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -31,6 +31,7 @@ pub(super) fn generate_invalidates<'tcx>( } if let Some(all_facts) = all_facts { + let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); let dominators = body.dominators(); let mut ig = InvalidationGenerator { all_facts, diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index bbcb823c8f91c..9ea3bd8899b9d 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -201,6 +201,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( ); if let Some(all_facts) = &mut all_facts { + let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation"); all_facts .universal_region .extend(universal_regions.universal_regions()); @@ -302,6 +303,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( .unwrap_or_else(|_| String::from("Naive")); let algorithm = Algorithm::from_str(&algorithm).unwrap(); debug!("compute_regions: using polonius algorithm {:?}", algorithm); + let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis"); Some(Rc::new(Output::compute( &all_facts, algorithm, diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs index 810811f9f5cf3..0354b0d6b92c5 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs @@ -5,20 +5,19 @@ use crate::util::liveness::{categorize, DefUse}; use rustc::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc::mir::{Local, Location, Place, ReadOnlyBodyAndCache}; use rustc::ty::subst::GenericArg; -use rustc::ty::Ty; use super::TypeChecker; -type VarPointRelations = Vec<(Local, LocationIndex)>; -type MovePathPointRelations = Vec<(MovePathIndex, LocationIndex)>; +type VarPointRelation = Vec<(Local, LocationIndex)>; +type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>; struct UseFactsExtractor<'me> { - var_defined: &'me mut VarPointRelations, - var_used: &'me mut VarPointRelations, + var_defined: &'me mut VarPointRelation, + var_used: &'me mut VarPointRelation, location_table: &'me LocationTable, var_drop_used: &'me mut Vec<(Local, Location)>, move_data: &'me MoveData<'me>, - path_accessed_at: &'me mut MovePathPointRelations, + path_accessed_at: &'me mut PathPointRelation, } // A Visitor to walk through the MIR and extract point-wise facts @@ -28,22 +27,22 @@ impl UseFactsExtractor<'_> { } fn insert_def(&mut self, local: Local, location: Location) { - debug!("LivenessFactsExtractor::insert_def()"); + debug!("UseFactsExtractor::insert_def()"); self.var_defined.push((local, self.location_to_index(location))); } fn insert_use(&mut self, local: Local, location: Location) { - debug!("LivenessFactsExtractor::insert_use()"); + debug!("UseFactsExtractor::insert_use()"); self.var_used.push((local, self.location_to_index(location))); } fn insert_drop_use(&mut self, local: Local, location: Location) { - debug!("LivenessFactsExtractor::insert_drop_use()"); + debug!("UseFactsExtractor::insert_drop_use()"); self.var_drop_used.push((local, location)); } fn insert_path_access(&mut self, path: MovePathIndex, location: Location) { - debug!("LivenessFactsExtractor::insert_path_access({:?}, {:?})", path, location); + debug!("UseFactsExtractor::insert_path_access({:?}, {:?})", path, location); self.path_accessed_at.push((path, self.location_to_index(location))); } @@ -84,17 +83,6 @@ impl Visitor<'tcx> for UseFactsExtractor<'_> { } } -fn add_var_uses_regions(typeck: &mut TypeChecker<'_, 'tcx>, local: Local, ty: Ty<'tcx>) { - debug!("add_regions(local={:?}, type={:?})", local, ty); - typeck.tcx().for_each_free_region(&ty, |region| { - let region_vid = typeck.borrowck_context.universal_regions.to_region_vid(region); - debug!("add_regions for region {:?}", region_vid); - if let Some(facts) = typeck.borrowck_context.all_facts { - facts.var_uses_region.push((local, region_vid)); - } - }); -} - pub(super) fn populate_access_facts( typeck: &mut TypeChecker<'_, 'tcx>, body: ReadOnlyBodyAndCache<'_, 'tcx>, @@ -102,26 +90,32 @@ pub(super) fn populate_access_facts( move_data: &MoveData<'_>, drop_used: &mut Vec<(Local, Location)>, ) { - debug!("populate_var_liveness_facts()"); + debug!("populate_access_facts()"); if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() { - UseFactsExtractor { + let mut extractor = UseFactsExtractor { var_defined: &mut facts.var_defined, var_used: &mut facts.var_used, var_drop_used: drop_used, path_accessed_at: &mut facts.path_accessed_at, location_table, move_data, - } - .visit_body(body); + }; + extractor.visit_body(body); facts.var_drop_used.extend(drop_used.iter().map(|&(local, location)| { (local, location_table.mid_index(location)) })); - } - for (local, local_decl) in body.local_decls.iter_enumerated() { - add_var_uses_regions(typeck, local, local_decl.ty); + for (local, local_decl) in body.local_decls.iter_enumerated() { + debug!("add var_uses_regions facts - local={:?}, type={:?}", local, local_decl.ty); + let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); + let universal_regions = &typeck.borrowck_context.universal_regions; + typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| { + let region_vid = universal_regions.to_region_vid(region); + facts.var_uses_region.push((local, region_vid)); + }); + } } } @@ -133,12 +127,12 @@ pub(super) fn add_var_drops_regions( kind: &GenericArg<'tcx>, ) { debug!("add_var_drops_region(local={:?}, kind={:?}", local, kind); - let tcx = typeck.tcx(); - - tcx.for_each_free_region(kind, |drop_live_region| { - let region_vid = typeck.borrowck_context.universal_regions.to_region_vid(drop_live_region); - if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() { + if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() { + let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); + let universal_regions = &typeck.borrowck_context.universal_regions; + typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| { + let region_vid = universal_regions.to_region_vid(drop_live_region); facts.var_drops_region.push((local, region_vid)); - }; - }); + }); + } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 8d4e76cadbfc2..cddc3b4a271d4 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -182,7 +182,7 @@ pub(crate) fn type_check<'tcx>( move_data, location_table); - translate_outlives_facts(cx.borrowck_context); + translate_outlives_facts(&mut cx); }, ); @@ -228,8 +228,10 @@ fn type_check_internal<'a, 'tcx, R>( extra(&mut checker) } -fn translate_outlives_facts(cx: &mut BorrowCheckContext<'_, '_>) { +fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { + let cx = &mut typeck.borrowck_context; if let Some(facts) = cx.all_facts { + let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation"); let location_table = cx.location_table; facts .outlives @@ -2489,6 +2491,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // that occurs when we are borrowing an unsafe place, for // example). if let Some(all_facts) = all_facts { + let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); if let Some(borrow_index) = borrow_set.location_map.get(&location) { let region_vid = borrow_region.to_region_vid(); all_facts.borrow_region.push(( diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index eef39f8040ea0..628ac721adc59 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -29,7 +29,7 @@ use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; -use rustc::ty::TyCtxt; +use rustc::ty::{self, TyCtxt}; use rustc::mir::*; use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext, MutatingUseContext}; use std::borrow::Cow; @@ -367,9 +367,14 @@ impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> { if let StatementKind::Assign( box (p, Rvalue::Use(Operand::Constant(c))) ) = &stmt.kind { - if !p.is_indirect() { - trace!("skipping store of const value {:?} to {:?}", c, p); - return; + match c.literal.val { + // Keep assignments from unevaluated constants around, since the evaluation + // may report errors, even if the use of the constant is dead code. + ty::ConstKind::Unevaluated(..) => {} + _ => if !p.is_indirect() { + trace!("skipping store of const value {:?} to {:?}", c, p); + return; + }, } } } diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index b7f5ed9d004d4..e198b008753ed 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -1,10 +1,24 @@ use rustc::hir; use rustc::traits::auto_trait::{self, AutoTraitResult}; -use rustc::ty::{self, TypeFoldable}; +use rustc::ty::{self, Region, RegionVid, TypeFoldable}; +use rustc::util::nodemap::FxHashSet; + use std::fmt::Debug; use super::*; +#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] +enum RegionTarget<'tcx> { + Region(Region<'tcx>), + RegionVid(RegionVid) +} + +#[derive(Default, Debug, Clone)] +struct RegionDeps<'tcx> { + larger: FxHashSet>, + smaller: FxHashSet> +} + pub struct AutoTraitFinder<'a, 'tcx> { pub cx: &'a core::DocContext<'tcx>, pub f: auto_trait::AutoTraitFinder<'tcx>, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 327be40a58f2f..e5f684cbca87b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1,10 +1,9 @@ -// ignore-tidy-filelength - //! This module contains the "cleaned" pieces of the AST, and the functions //! that clean them. pub mod inline; pub mod cfg; +pub mod utils; mod auto_trait; mod blanket_impl; mod simplify; @@ -19,15 +18,15 @@ use rustc::middle::stability; use rustc::mir::interpret::GlobalId; use rustc::hir; use rustc::hir::def::{CtorKind, DefKind, Res}; -use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc::hir::ptr::P; -use rustc::ty::subst::{InternalSubsts, SubstsRef, GenericArgKind}; -use rustc::ty::{self, DefIdTree, TyCtxt, Region, RegionVid, Ty, AdtKind}; +use rustc::ty::subst::InternalSubsts; +use rustc::ty::{self, TyCtxt, Ty, AdtKind}; use rustc::ty::fold::TypeFolder; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use syntax::ast::{self, Ident}; use syntax::attr; -use syntax_pos::symbol::{Symbol, kw, sym}; +use syntax_pos::symbol::{kw, sym}; use syntax_pos::hygiene::MacroKind; use syntax_pos::{self, Pos}; @@ -41,8 +40,9 @@ use std::u32; use crate::core::{self, DocContext, ImplTraitParam}; use crate::doctree; -use self::auto_trait::AutoTraitFinder; -use self::blanket_impl::BlanketImplFinder; +use utils::*; + +pub use utils::{get_auto_trait_and_blanket_impls, krate, register_res}; pub use self::types::*; pub use self::types::Type::*; @@ -54,15 +54,6 @@ pub use self::types::Visibility::{Public, Inherited}; const FN_OUTPUT_NAME: &'static str = "Output"; -// extract the stability index for a node from tcx, if possible -fn get_stability(cx: &DocContext<'_>, def_id: DefId) -> Option { - cx.tcx.lookup_stability(def_id).clean(cx) -} - -fn get_deprecation(cx: &DocContext<'_>, def_id: DefId) -> Option { - cx.tcx.lookup_deprecation(def_id).clean(cx) -} - pub trait Clean { fn clean(&self, cx: &DocContext<'_>) -> T; } @@ -109,91 +100,6 @@ impl, U> Clean> for P<[T]> { } } -pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { - use crate::visit_lib::LibEmbargoVisitor; - - let krate = cx.tcx.hir().krate(); - let module = crate::visit_ast::RustdocVisitor::new(&mut cx).visit(krate); - - let mut r = cx.renderinfo.get_mut(); - r.deref_trait_did = cx.tcx.lang_items().deref_trait(); - r.deref_mut_trait_did = cx.tcx.lang_items().deref_mut_trait(); - r.owned_box_did = cx.tcx.lang_items().owned_box(); - - let mut externs = Vec::new(); - for &cnum in cx.tcx.crates().iter() { - externs.push((cnum, cnum.clean(cx))); - // Analyze doc-reachability for extern items - LibEmbargoVisitor::new(&mut cx).visit_lib(cnum); - } - externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); - - // Clean the crate, translating the entire libsyntax AST to one that is - // understood by rustdoc. - let mut module = module.clean(cx); - let mut masked_crates = FxHashSet::default(); - - match module.inner { - ModuleItem(ref module) => { - for it in &module.items { - // `compiler_builtins` should be masked too, but we can't apply - // `#[doc(masked)]` to the injected `extern crate` because it's unstable. - if it.is_extern_crate() - && (it.attrs.has_doc_flag(sym::masked) - || cx.tcx.is_compiler_builtins(it.def_id.krate)) - { - masked_crates.insert(it.def_id.krate); - } - } - } - _ => unreachable!(), - } - - let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx); - { - let m = match module.inner { - ModuleItem(ref mut m) => m, - _ => unreachable!(), - }; - m.items.extend(primitives.iter().map(|&(def_id, prim, ref attrs)| { - Item { - source: Span::empty(), - name: Some(prim.to_url_str().to_string()), - attrs: attrs.clone(), - visibility: Public, - stability: get_stability(cx, def_id), - deprecation: get_deprecation(cx, def_id), - def_id, - inner: PrimitiveItem(prim), - } - })); - m.items.extend(keywords.into_iter().map(|(def_id, kw, attrs)| { - Item { - source: Span::empty(), - name: Some(kw.clone()), - attrs, - visibility: Public, - stability: get_stability(cx, def_id), - deprecation: get_deprecation(cx, def_id), - def_id, - inner: KeywordItem(kw), - } - })); - } - - Crate { - name, - version: None, - src, - module: Some(module), - externs, - primitives, - external_traits: cx.external_traits.clone(), - masked_crates, - collapsed: false, - } -} - impl Clean for CrateNum { fn clean(&self, cx: &DocContext<'_>) -> ExternalCrate { let root = DefId { krate: *self, index: CRATE_DEF_INDEX }; @@ -394,66 +300,6 @@ impl Clean for hir::GenericBound { } } -fn external_generic_args( - cx: &DocContext<'_>, - trait_did: Option, - has_self: bool, - bindings: Vec, - substs: SubstsRef<'_>, -) -> GenericArgs { - let mut skip_self = has_self; - let mut ty_kind = None; - let args: Vec<_> = substs.iter().filter_map(|kind| match kind.unpack() { - GenericArgKind::Lifetime(lt) => { - lt.clean(cx).and_then(|lt| Some(GenericArg::Lifetime(lt))) - } - GenericArgKind::Type(_) if skip_self => { - skip_self = false; - None - } - GenericArgKind::Type(ty) => { - ty_kind = Some(&ty.kind); - Some(GenericArg::Type(ty.clean(cx))) - } - GenericArgKind::Const(ct) => Some(GenericArg::Const(ct.clean(cx))), - }).collect(); - - match trait_did { - // Attempt to sugar an external path like Fn<(A, B,), C> to Fn(A, B) -> C - Some(did) if cx.tcx.lang_items().fn_trait_kind(did).is_some() => { - assert!(ty_kind.is_some()); - let inputs = match ty_kind { - Some(ty::Tuple(ref tys)) => tys.iter().map(|t| t.expect_ty().clean(cx)).collect(), - _ => return GenericArgs::AngleBracketed { args, bindings }, - }; - let output = None; - // FIXME(#20299) return type comes from a projection now - // match types[1].kind { - // ty::Tuple(ref v) if v.is_empty() => None, // -> () - // _ => Some(types[1].clean(cx)) - // }; - GenericArgs::Parenthesized { inputs, output } - }, - _ => { - GenericArgs::AngleBracketed { args, bindings } - } - } -} - -// trait_did should be set to a trait's DefId if called on a TraitRef, in order to sugar -// from Fn<(A, B,), C> to Fn(A, B) -> C -fn external_path(cx: &DocContext<'_>, name: Symbol, trait_did: Option, has_self: bool, - bindings: Vec, substs: SubstsRef<'_>) -> Path { - Path { - global: false, - res: Res::Err, - segments: vec![PathSegment { - name: name.to_string(), - args: external_generic_args(cx, trait_did, has_self, bindings, substs) - }], - } -} - impl<'a, 'tcx> Clean for (&'a ty::TraitRef<'tcx>, Vec) { fn clean(&self, cx: &DocContext<'_>) -> GenericBound { let (trait_ref, ref bounds) = *self; @@ -1036,118 +882,6 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx } } -/// The point of this function is to replace bounds with types. -/// -/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option` will return -/// `[Display, Option]` (we just returns the list of the types, we don't care about the -/// wrapped types in here). -fn get_real_types( - generics: &Generics, - arg: &Type, - cx: &DocContext<'_>, - recurse: i32, -) -> FxHashSet { - let arg_s = arg.print().to_string(); - let mut res = FxHashSet::default(); - if recurse >= 10 { // FIXME: remove this whole recurse thing when the recursion bug is fixed - return res; - } - if arg.is_full_generic() { - if let Some(where_pred) = generics.where_predicates.iter().find(|g| { - match g { - &WherePredicate::BoundPredicate { ref ty, .. } => ty.def_id() == arg.def_id(), - _ => false, - } - }) { - let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); - for bound in bounds.iter() { - match *bound { - GenericBound::TraitBound(ref poly_trait, _) => { - for x in poly_trait.generic_params.iter() { - if !x.is_type() { - continue - } - if let Some(ty) = x.get_type() { - let adds = get_real_types(generics, &ty, cx, recurse + 1); - if !adds.is_empty() { - res.extend(adds); - } else if !ty.is_full_generic() { - res.insert(ty); - } - } - } - } - _ => {} - } - } - } - if let Some(bound) = generics.params.iter().find(|g| { - g.is_type() && g.name == arg_s - }) { - for bound in bound.get_bounds().unwrap_or_else(|| &[]) { - if let Some(ty) = bound.get_trait_type() { - let adds = get_real_types(generics, &ty, cx, recurse + 1); - if !adds.is_empty() { - res.extend(adds); - } else if !ty.is_full_generic() { - res.insert(ty.clone()); - } - } - } - } - } else { - res.insert(arg.clone()); - if let Some(gens) = arg.generics() { - for gen in gens.iter() { - if gen.is_full_generic() { - let adds = get_real_types(generics, gen, cx, recurse + 1); - if !adds.is_empty() { - res.extend(adds); - } - } else { - res.insert(gen.clone()); - } - } - } - } - res -} - -/// Return the full list of types when bounds have been resolved. -/// -/// i.e. `fn foo>(x: u32, y: B)` will return -/// `[u32, Display, Option]`. -pub fn get_all_types( - generics: &Generics, - decl: &FnDecl, - cx: &DocContext<'_>, -) -> (Vec, Vec) { - let mut all_types = FxHashSet::default(); - for arg in decl.inputs.values.iter() { - if arg.type_.is_self_type() { - continue; - } - let args = get_real_types(generics, &arg.type_, cx, 0); - if !args.is_empty() { - all_types.extend(args); - } else { - all_types.insert(arg.type_.clone()); - } - } - - let ret_types = match decl.output { - FunctionRetTy::Return(ref return_type) => { - let mut ret = get_real_types(generics, &return_type, cx, 0); - if ret.is_empty() { - ret.insert(return_type.clone()); - } - ret.into_iter().collect() - } - _ => Vec::new(), - }; - (all_types.into_iter().collect(), ret_types) -} - impl<'a> Clean for (&'a hir::FnSig, &'a hir::Generics, hir::BodyId, Option) { fn clean(&self, cx: &DocContext<'_>) -> Method { @@ -2262,66 +1996,6 @@ impl Clean for hir::PathSegment { } } -fn strip_type(ty: Type) -> Type { - match ty { - Type::ResolvedPath { path, param_names, did, is_generic } => { - Type::ResolvedPath { path: strip_path(&path), param_names, did, is_generic } - } - Type::Tuple(inner_tys) => { - Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect()) - } - Type::Slice(inner_ty) => Type::Slice(Box::new(strip_type(*inner_ty))), - Type::Array(inner_ty, s) => Type::Array(Box::new(strip_type(*inner_ty)), s), - Type::RawPointer(m, inner_ty) => Type::RawPointer(m, Box::new(strip_type(*inner_ty))), - Type::BorrowedRef { lifetime, mutability, type_ } => { - Type::BorrowedRef { lifetime, mutability, type_: Box::new(strip_type(*type_)) } - } - Type::QPath { name, self_type, trait_ } => { - Type::QPath { - name, - self_type: Box::new(strip_type(*self_type)), trait_: Box::new(strip_type(*trait_)) - } - } - _ => ty - } -} - -fn strip_path(path: &Path) -> Path { - let segments = path.segments.iter().map(|s| { - PathSegment { - name: s.name.clone(), - args: GenericArgs::AngleBracketed { - args: vec![], - bindings: vec![], - } - } - }).collect(); - - Path { - global: path.global, - res: path.res.clone(), - segments, - } -} - -fn qpath_to_string(p: &hir::QPath) -> String { - let segments = match *p { - hir::QPath::Resolved(_, ref path) => &path.segments, - hir::QPath::TypeRelative(_, ref segment) => return segment.ident.to_string(), - }; - - let mut s = String::new(); - for (i, seg) in segments.iter().enumerate() { - if i > 0 { - s.push_str("::"); - } - if seg.ident.name != kw::PathRoot { - s.push_str(&seg.ident.as_str()); - } - } - s -} - impl Clean for Ident { #[inline] fn clean(&self, cx: &DocContext<'_>) -> String { @@ -2444,15 +2118,6 @@ impl Clean for ty::ImplPolarity { } } -pub fn get_auto_trait_and_blanket_impls( - cx: &DocContext<'tcx>, - ty: Ty<'tcx>, - param_env_def_id: DefId, -) -> impl Iterator { - AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id).into_iter() - .chain(BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id)) -} - impl Clean> for doctree::Impl<'_> { fn clean(&self, cx: &DocContext<'_>) -> Vec { let mut ret = Vec::new(); @@ -2497,63 +2162,6 @@ impl Clean> for doctree::Impl<'_> { } } -fn build_deref_target_impls(cx: &DocContext<'_>, - items: &[Item], - ret: &mut Vec) { - use self::PrimitiveType::*; - let tcx = cx.tcx; - - for item in items { - let target = match item.inner { - TypedefItem(ref t, true) => &t.type_, - _ => continue, - }; - let primitive = match *target { - ResolvedPath { did, .. } if did.is_local() => continue, - ResolvedPath { did, .. } => { - ret.extend(inline::build_impls(cx, did, None)); - continue - } - _ => match target.primitive_type() { - Some(prim) => prim, - None => continue, - } - }; - let did = match primitive { - Isize => tcx.lang_items().isize_impl(), - I8 => tcx.lang_items().i8_impl(), - I16 => tcx.lang_items().i16_impl(), - I32 => tcx.lang_items().i32_impl(), - I64 => tcx.lang_items().i64_impl(), - I128 => tcx.lang_items().i128_impl(), - Usize => tcx.lang_items().usize_impl(), - U8 => tcx.lang_items().u8_impl(), - U16 => tcx.lang_items().u16_impl(), - U32 => tcx.lang_items().u32_impl(), - U64 => tcx.lang_items().u64_impl(), - U128 => tcx.lang_items().u128_impl(), - F32 => tcx.lang_items().f32_impl(), - F64 => tcx.lang_items().f64_impl(), - Char => tcx.lang_items().char_impl(), - Bool => tcx.lang_items().bool_impl(), - Str => tcx.lang_items().str_impl(), - Slice => tcx.lang_items().slice_impl(), - Array => tcx.lang_items().slice_impl(), - Tuple => None, - Unit => None, - RawPointer => tcx.lang_items().const_ptr_impl(), - Reference => None, - Fn => None, - Never => None, - }; - if let Some(did) = did { - if !did.is_local() { - inline::build_impl(cx, did, None, ret); - } - } - } -} - impl Clean> for doctree::ExternCrate<'_> { fn clean(&self, cx: &DocContext<'_>) -> Vec { @@ -2710,170 +2318,6 @@ impl Clean for doctree::ForeignItem<'_> { } } -// Utilities - -pub trait ToSource { - fn to_src(&self, cx: &DocContext<'_>) -> String; -} - -impl ToSource for syntax_pos::Span { - fn to_src(&self, cx: &DocContext<'_>) -> String { - debug!("converting span {:?} to snippet", self.clean(cx)); - let sn = match cx.sess().source_map().span_to_snippet(*self) { - Ok(x) => x, - Err(_) => String::new() - }; - debug!("got snippet {}", sn); - sn - } -} - -fn name_from_pat(p: &hir::Pat) -> String { - use rustc::hir::*; - debug!("trying to get a name from pattern: {:?}", p); - - match p.kind { - PatKind::Wild => "_".to_string(), - PatKind::Binding(_, _, ident, _) => ident.to_string(), - PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), - PatKind::Struct(ref name, ref fields, etc) => { - format!("{} {{ {}{} }}", qpath_to_string(name), - fields.iter().map(|fp| format!("{}: {}", fp.ident, name_from_pat(&fp.pat))) - .collect::>().join(", "), - if etc { ", .." } else { "" } - ) - } - PatKind::Or(ref pats) => { - pats.iter().map(|p| name_from_pat(&**p)).collect::>().join(" | ") - } - PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p)) - .collect::>().join(", ")), - PatKind::Box(ref p) => name_from_pat(&**p), - PatKind::Ref(ref p, _) => name_from_pat(&**p), - PatKind::Lit(..) => { - warn!("tried to get argument name from PatKind::Lit, \ - which is silly in function arguments"); - "()".to_string() - }, - PatKind::Range(..) => panic!("tried to get argument name from PatKind::Range, \ - which is not allowed in function arguments"), - PatKind::Slice(ref begin, ref mid, ref end) => { - let begin = begin.iter().map(|p| name_from_pat(&**p)); - let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter(); - let end = end.iter().map(|p| name_from_pat(&**p)); - format!("[{}]", begin.chain(mid).chain(end).collect::>().join(", ")) - }, - } -} - -fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String { - match n.val { - ty::ConstKind::Unevaluated(def_id, _) => { - if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(def_id) { - print_const_expr(cx, cx.tcx.hir().body_owned_by(hir_id)) - } else { - inline::print_inlined_const(cx, def_id) - } - }, - _ => { - let mut s = n.to_string(); - // array lengths are obviously usize - if s.ends_with("usize") { - let n = s.len() - "usize".len(); - s.truncate(n); - if s.ends_with(": ") { - let n = s.len() - ": ".len(); - s.truncate(n); - } - } - s - }, - } -} - -fn print_const_expr(cx: &DocContext<'_>, body: hir::BodyId) -> String { - cx.tcx.hir().hir_to_pretty_string(body.hir_id) -} - -/// Given a type Path, resolve it to a Type using the TyCtxt -fn resolve_type(cx: &DocContext<'_>, - path: Path, - id: hir::HirId) -> Type { - if id == hir::DUMMY_HIR_ID { - debug!("resolve_type({:?})", path); - } else { - debug!("resolve_type({:?},{:?})", path, id); - } - - let is_generic = match path.res { - Res::PrimTy(p) => match p { - hir::Str => return Primitive(PrimitiveType::Str), - hir::Bool => return Primitive(PrimitiveType::Bool), - hir::Char => return Primitive(PrimitiveType::Char), - hir::Int(int_ty) => return Primitive(int_ty.into()), - hir::Uint(uint_ty) => return Primitive(uint_ty.into()), - hir::Float(float_ty) => return Primitive(float_ty.into()), - }, - Res::SelfTy(..) if path.segments.len() == 1 => { - return Generic(kw::SelfUpper.to_string()); - } - Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => { - return Generic(format!("{:#}", path.print())); - } - Res::SelfTy(..) - | Res::Def(DefKind::TyParam, _) - | Res::Def(DefKind::AssocTy, _) => true, - _ => false, - }; - let did = register_res(&*cx, path.res); - ResolvedPath { path, param_names: None, did, is_generic } -} - -pub fn register_res(cx: &DocContext<'_>, res: Res) -> DefId { - debug!("register_res({:?})", res); - - let (did, kind) = match res { - Res::Def(DefKind::Fn, i) => (i, TypeKind::Function), - Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef), - Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum), - Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait), - Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct), - Res::Def(DefKind::Union, i) => (i, TypeKind::Union), - Res::Def(DefKind::Mod, i) => (i, TypeKind::Module), - Res::Def(DefKind::ForeignTy, i) => (i, TypeKind::Foreign), - Res::Def(DefKind::Const, i) => (i, TypeKind::Const), - Res::Def(DefKind::Static, i) => (i, TypeKind::Static), - Res::Def(DefKind::Variant, i) => (cx.tcx.parent(i).expect("cannot get parent def id"), - TypeKind::Enum), - Res::Def(DefKind::Macro(mac_kind), i) => match mac_kind { - MacroKind::Bang => (i, TypeKind::Macro), - MacroKind::Attr => (i, TypeKind::Attr), - MacroKind::Derive => (i, TypeKind::Derive), - }, - Res::Def(DefKind::TraitAlias, i) => (i, TypeKind::TraitAlias), - Res::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait), - Res::SelfTy(_, Some(impl_def_id)) => return impl_def_id, - _ => return res.def_id() - }; - if did.is_local() { return did } - inline::record_extern_fqn(cx, did, kind); - if let TypeKind::Trait = kind { - inline::record_extern_trait(cx, did); - } - did -} - -fn resolve_use_source(cx: &DocContext<'_>, path: Path) -> ImportSource { - ImportSource { - did: if path.res.opt_def_id().is_none() { - None - } else { - Some(register_res(cx, path.res)) - }, - path, - } -} - impl Clean for doctree::Macro<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let name = self.name.clean(cx); @@ -2981,29 +2425,6 @@ impl Clean for hir::TypeBindingKind { } } -pub fn enter_impl_trait(cx: &DocContext<'_>, f: F) -> R -where - F: FnOnce() -> R, -{ - let old_bounds = mem::take(&mut *cx.impl_trait_bounds.borrow_mut()); - let r = f(); - assert!(cx.impl_trait_bounds.borrow().is_empty()); - *cx.impl_trait_bounds.borrow_mut() = old_bounds; - r -} - -#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] -enum RegionTarget<'tcx> { - Region(Region<'tcx>), - RegionVid(RegionVid) -} - -#[derive(Default, Debug, Clone)] -struct RegionDeps<'tcx> { - larger: FxHashSet>, - smaller: FxHashSet> -} - enum SimpleBound { TraitBound(Vec, Vec, Vec, hir::TraitBoundModifier), Outlives(Lifetime), diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs new file mode 100644 index 0000000000000..e46300a8a5713 --- /dev/null +++ b/src/librustdoc/clean/utils.rs @@ -0,0 +1,585 @@ +use crate::core::DocContext; +use crate::clean::{ + Clean, Crate, Deprecation, ExternalCrate, FnDecl, FunctionRetTy, Generic, GenericArg, + GenericArgs, Generics, GenericBound, GetDefId, ImportSource, Item, ItemEnum, MacroKind, Path, + PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type, TypeBinding, + TypeKind, Visibility, WherePredicate, inline, +}; +use crate::clean::blanket_impl::BlanketImplFinder; +use crate::clean::auto_trait::AutoTraitFinder; + +use rustc::hir; +use rustc::hir::def::{DefKind, Res}; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::ty::{self, DefIdTree, Ty}; +use rustc::ty::subst::{SubstsRef, GenericArgKind}; +use rustc::util::nodemap::FxHashSet; +use syntax_pos; +use syntax_pos::symbol::{Symbol, kw, sym}; + +use std::mem; + +pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { + use crate::visit_lib::LibEmbargoVisitor; + + let krate = cx.tcx.hir().krate(); + let module = crate::visit_ast::RustdocVisitor::new(&mut cx).visit(krate); + + let mut r = cx.renderinfo.get_mut(); + r.deref_trait_did = cx.tcx.lang_items().deref_trait(); + r.deref_mut_trait_did = cx.tcx.lang_items().deref_mut_trait(); + r.owned_box_did = cx.tcx.lang_items().owned_box(); + + let mut externs = Vec::new(); + for &cnum in cx.tcx.crates().iter() { + externs.push((cnum, cnum.clean(cx))); + // Analyze doc-reachability for extern items + LibEmbargoVisitor::new(&mut cx).visit_lib(cnum); + } + externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); + + // Clean the crate, translating the entire libsyntax AST to one that is + // understood by rustdoc. + let mut module = module.clean(cx); + let mut masked_crates = FxHashSet::default(); + + match module.inner { + ItemEnum::ModuleItem(ref module) => { + for it in &module.items { + // `compiler_builtins` should be masked too, but we can't apply + // `#[doc(masked)]` to the injected `extern crate` because it's unstable. + if it.is_extern_crate() + && (it.attrs.has_doc_flag(sym::masked) + || cx.tcx.is_compiler_builtins(it.def_id.krate)) + { + masked_crates.insert(it.def_id.krate); + } + } + } + _ => unreachable!(), + } + + let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx); + { + let m = match module.inner { + ItemEnum::ModuleItem(ref mut m) => m, + _ => unreachable!(), + }; + m.items.extend(primitives.iter().map(|&(def_id, prim, ref attrs)| { + Item { + source: Span::empty(), + name: Some(prim.to_url_str().to_string()), + attrs: attrs.clone(), + visibility: Visibility::Public, + stability: get_stability(cx, def_id), + deprecation: get_deprecation(cx, def_id), + def_id, + inner: ItemEnum::PrimitiveItem(prim), + } + })); + m.items.extend(keywords.into_iter().map(|(def_id, kw, attrs)| { + Item { + source: Span::empty(), + name: Some(kw.clone()), + attrs, + visibility: Visibility::Public, + stability: get_stability(cx, def_id), + deprecation: get_deprecation(cx, def_id), + def_id, + inner: ItemEnum::KeywordItem(kw), + } + })); + } + + Crate { + name, + version: None, + src, + module: Some(module), + externs, + primitives, + external_traits: cx.external_traits.clone(), + masked_crates, + collapsed: false, + } +} + +// extract the stability index for a node from tcx, if possible +pub fn get_stability(cx: &DocContext<'_>, def_id: DefId) -> Option { + cx.tcx.lookup_stability(def_id).clean(cx) +} + +pub fn get_deprecation(cx: &DocContext<'_>, def_id: DefId) -> Option { + cx.tcx.lookup_deprecation(def_id).clean(cx) +} + +pub fn external_generic_args( + cx: &DocContext<'_>, + trait_did: Option, + has_self: bool, + bindings: Vec, + substs: SubstsRef<'_>, +) -> GenericArgs { + let mut skip_self = has_self; + let mut ty_kind = None; + let args: Vec<_> = substs.iter().filter_map(|kind| match kind.unpack() { + GenericArgKind::Lifetime(lt) => { + lt.clean(cx).and_then(|lt| Some(GenericArg::Lifetime(lt))) + } + GenericArgKind::Type(_) if skip_self => { + skip_self = false; + None + } + GenericArgKind::Type(ty) => { + ty_kind = Some(&ty.kind); + Some(GenericArg::Type(ty.clean(cx))) + } + GenericArgKind::Const(ct) => Some(GenericArg::Const(ct.clean(cx))), + }).collect(); + + match trait_did { + // Attempt to sugar an external path like Fn<(A, B,), C> to Fn(A, B) -> C + Some(did) if cx.tcx.lang_items().fn_trait_kind(did).is_some() => { + assert!(ty_kind.is_some()); + let inputs = match ty_kind { + Some(ty::Tuple(ref tys)) => tys.iter().map(|t| t.expect_ty().clean(cx)).collect(), + _ => return GenericArgs::AngleBracketed { args, bindings }, + }; + let output = None; + // FIXME(#20299) return type comes from a projection now + // match types[1].kind { + // ty::Tuple(ref v) if v.is_empty() => None, // -> () + // _ => Some(types[1].clean(cx)) + // }; + GenericArgs::Parenthesized { inputs, output } + }, + _ => { + GenericArgs::AngleBracketed { args, bindings } + } + } +} + +// trait_did should be set to a trait's DefId if called on a TraitRef, in order to sugar +// from Fn<(A, B,), C> to Fn(A, B) -> C +pub fn external_path(cx: &DocContext<'_>, name: Symbol, trait_did: Option, has_self: bool, + bindings: Vec, substs: SubstsRef<'_>) -> Path { + Path { + global: false, + res: Res::Err, + segments: vec![PathSegment { + name: name.to_string(), + args: external_generic_args(cx, trait_did, has_self, bindings, substs) + }], + } +} + +/// The point of this function is to replace bounds with types. +/// +/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option` will return +/// `[Display, Option]` (we just returns the list of the types, we don't care about the +/// wrapped types in here). +pub fn get_real_types( + generics: &Generics, + arg: &Type, + cx: &DocContext<'_>, + recurse: i32, +) -> FxHashSet { + let arg_s = arg.print().to_string(); + let mut res = FxHashSet::default(); + if recurse >= 10 { // FIXME: remove this whole recurse thing when the recursion bug is fixed + return res; + } + if arg.is_full_generic() { + if let Some(where_pred) = generics.where_predicates.iter().find(|g| { + match g { + &WherePredicate::BoundPredicate { ref ty, .. } => ty.def_id() == arg.def_id(), + _ => false, + } + }) { + let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); + for bound in bounds.iter() { + match *bound { + GenericBound::TraitBound(ref poly_trait, _) => { + for x in poly_trait.generic_params.iter() { + if !x.is_type() { + continue + } + if let Some(ty) = x.get_type() { + let adds = get_real_types(generics, &ty, cx, recurse + 1); + if !adds.is_empty() { + res.extend(adds); + } else if !ty.is_full_generic() { + res.insert(ty); + } + } + } + } + _ => {} + } + } + } + if let Some(bound) = generics.params.iter().find(|g| { + g.is_type() && g.name == arg_s + }) { + for bound in bound.get_bounds().unwrap_or_else(|| &[]) { + if let Some(ty) = bound.get_trait_type() { + let adds = get_real_types(generics, &ty, cx, recurse + 1); + if !adds.is_empty() { + res.extend(adds); + } else if !ty.is_full_generic() { + res.insert(ty.clone()); + } + } + } + } + } else { + res.insert(arg.clone()); + if let Some(gens) = arg.generics() { + for gen in gens.iter() { + if gen.is_full_generic() { + let adds = get_real_types(generics, gen, cx, recurse + 1); + if !adds.is_empty() { + res.extend(adds); + } + } else { + res.insert(gen.clone()); + } + } + } + } + res +} + +/// Return the full list of types when bounds have been resolved. +/// +/// i.e. `fn foo>(x: u32, y: B)` will return +/// `[u32, Display, Option]`. +pub fn get_all_types( + generics: &Generics, + decl: &FnDecl, + cx: &DocContext<'_>, +) -> (Vec, Vec) { + let mut all_types = FxHashSet::default(); + for arg in decl.inputs.values.iter() { + if arg.type_.is_self_type() { + continue; + } + let args = get_real_types(generics, &arg.type_, cx, 0); + if !args.is_empty() { + all_types.extend(args); + } else { + all_types.insert(arg.type_.clone()); + } + } + + let ret_types = match decl.output { + FunctionRetTy::Return(ref return_type) => { + let mut ret = get_real_types(generics, &return_type, cx, 0); + if ret.is_empty() { + ret.insert(return_type.clone()); + } + ret.into_iter().collect() + } + _ => Vec::new(), + }; + (all_types.into_iter().collect(), ret_types) +} + +pub fn strip_type(ty: Type) -> Type { + match ty { + Type::ResolvedPath { path, param_names, did, is_generic } => { + Type::ResolvedPath { path: strip_path(&path), param_names, did, is_generic } + } + Type::Tuple(inner_tys) => { + Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect()) + } + Type::Slice(inner_ty) => Type::Slice(Box::new(strip_type(*inner_ty))), + Type::Array(inner_ty, s) => Type::Array(Box::new(strip_type(*inner_ty)), s), + Type::RawPointer(m, inner_ty) => Type::RawPointer(m, Box::new(strip_type(*inner_ty))), + Type::BorrowedRef { lifetime, mutability, type_ } => { + Type::BorrowedRef { lifetime, mutability, type_: Box::new(strip_type(*type_)) } + } + Type::QPath { name, self_type, trait_ } => { + Type::QPath { + name, + self_type: Box::new(strip_type(*self_type)), trait_: Box::new(strip_type(*trait_)) + } + } + _ => ty + } +} + +pub fn strip_path(path: &Path) -> Path { + let segments = path.segments.iter().map(|s| { + PathSegment { + name: s.name.clone(), + args: GenericArgs::AngleBracketed { + args: vec![], + bindings: vec![], + } + } + }).collect(); + + Path { + global: path.global, + res: path.res.clone(), + segments, + } +} + +pub fn qpath_to_string(p: &hir::QPath) -> String { + let segments = match *p { + hir::QPath::Resolved(_, ref path) => &path.segments, + hir::QPath::TypeRelative(_, ref segment) => return segment.ident.to_string(), + }; + + let mut s = String::new(); + for (i, seg) in segments.iter().enumerate() { + if i > 0 { + s.push_str("::"); + } + if seg.ident.name != kw::PathRoot { + s.push_str(&seg.ident.as_str()); + } + } + s +} + +pub fn build_deref_target_impls(cx: &DocContext<'_>, + items: &[Item], + ret: &mut Vec) { + use self::PrimitiveType::*; + let tcx = cx.tcx; + + for item in items { + let target = match item.inner { + ItemEnum::TypedefItem(ref t, true) => &t.type_, + _ => continue, + }; + let primitive = match *target { + ResolvedPath { did, .. } if did.is_local() => continue, + ResolvedPath { did, .. } => { + ret.extend(inline::build_impls(cx, did, None)); + continue + } + _ => match target.primitive_type() { + Some(prim) => prim, + None => continue, + } + }; + let did = match primitive { + Isize => tcx.lang_items().isize_impl(), + I8 => tcx.lang_items().i8_impl(), + I16 => tcx.lang_items().i16_impl(), + I32 => tcx.lang_items().i32_impl(), + I64 => tcx.lang_items().i64_impl(), + I128 => tcx.lang_items().i128_impl(), + Usize => tcx.lang_items().usize_impl(), + U8 => tcx.lang_items().u8_impl(), + U16 => tcx.lang_items().u16_impl(), + U32 => tcx.lang_items().u32_impl(), + U64 => tcx.lang_items().u64_impl(), + U128 => tcx.lang_items().u128_impl(), + F32 => tcx.lang_items().f32_impl(), + F64 => tcx.lang_items().f64_impl(), + Char => tcx.lang_items().char_impl(), + Bool => tcx.lang_items().bool_impl(), + Str => tcx.lang_items().str_impl(), + Slice => tcx.lang_items().slice_impl(), + Array => tcx.lang_items().slice_impl(), + Tuple => None, + Unit => None, + RawPointer => tcx.lang_items().const_ptr_impl(), + Reference => None, + Fn => None, + Never => None, + }; + if let Some(did) = did { + if !did.is_local() { + inline::build_impl(cx, did, None, ret); + } + } + } +} + +pub trait ToSource { + fn to_src(&self, cx: &DocContext<'_>) -> String; +} + +impl ToSource for syntax_pos::Span { + fn to_src(&self, cx: &DocContext<'_>) -> String { + debug!("converting span {:?} to snippet", self.clean(cx)); + let sn = match cx.sess().source_map().span_to_snippet(*self) { + Ok(x) => x, + Err(_) => String::new() + }; + debug!("got snippet {}", sn); + sn + } +} + +pub fn name_from_pat(p: &hir::Pat) -> String { + use rustc::hir::*; + debug!("trying to get a name from pattern: {:?}", p); + + match p.kind { + PatKind::Wild => "_".to_string(), + PatKind::Binding(_, _, ident, _) => ident.to_string(), + PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), + PatKind::Struct(ref name, ref fields, etc) => { + format!("{} {{ {}{} }}", qpath_to_string(name), + fields.iter().map(|fp| format!("{}: {}", fp.ident, name_from_pat(&fp.pat))) + .collect::>().join(", "), + if etc { ", .." } else { "" } + ) + } + PatKind::Or(ref pats) => { + pats.iter().map(|p| name_from_pat(&**p)).collect::>().join(" | ") + } + PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p)) + .collect::>().join(", ")), + PatKind::Box(ref p) => name_from_pat(&**p), + PatKind::Ref(ref p, _) => name_from_pat(&**p), + PatKind::Lit(..) => { + warn!("tried to get argument name from PatKind::Lit, \ + which is silly in function arguments"); + "()".to_string() + }, + PatKind::Range(..) => panic!("tried to get argument name from PatKind::Range, \ + which is not allowed in function arguments"), + PatKind::Slice(ref begin, ref mid, ref end) => { + let begin = begin.iter().map(|p| name_from_pat(&**p)); + let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter(); + let end = end.iter().map(|p| name_from_pat(&**p)); + format!("[{}]", begin.chain(mid).chain(end).collect::>().join(", ")) + }, + } +} + +pub fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String { + match n.val { + ty::ConstKind::Unevaluated(def_id, _) => { + if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(def_id) { + print_const_expr(cx, cx.tcx.hir().body_owned_by(hir_id)) + } else { + inline::print_inlined_const(cx, def_id) + } + }, + _ => { + let mut s = n.to_string(); + // array lengths are obviously usize + if s.ends_with("usize") { + let n = s.len() - "usize".len(); + s.truncate(n); + if s.ends_with(": ") { + let n = s.len() - ": ".len(); + s.truncate(n); + } + } + s + }, + } +} + +pub fn print_const_expr(cx: &DocContext<'_>, body: hir::BodyId) -> String { + cx.tcx.hir().hir_to_pretty_string(body.hir_id) +} + +/// Given a type Path, resolve it to a Type using the TyCtxt +pub fn resolve_type(cx: &DocContext<'_>, + path: Path, + id: hir::HirId) -> Type { + if id == hir::DUMMY_HIR_ID { + debug!("resolve_type({:?})", path); + } else { + debug!("resolve_type({:?},{:?})", path, id); + } + + let is_generic = match path.res { + Res::PrimTy(p) => match p { + hir::Str => return Primitive(PrimitiveType::Str), + hir::Bool => return Primitive(PrimitiveType::Bool), + hir::Char => return Primitive(PrimitiveType::Char), + hir::Int(int_ty) => return Primitive(int_ty.into()), + hir::Uint(uint_ty) => return Primitive(uint_ty.into()), + hir::Float(float_ty) => return Primitive(float_ty.into()), + }, + Res::SelfTy(..) if path.segments.len() == 1 => { + return Generic(kw::SelfUpper.to_string()); + } + Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => { + return Generic(format!("{:#}", path.print())); + } + Res::SelfTy(..) + | Res::Def(DefKind::TyParam, _) + | Res::Def(DefKind::AssocTy, _) => true, + _ => false, + }; + let did = register_res(&*cx, path.res); + ResolvedPath { path, param_names: None, did, is_generic } +} + +pub fn get_auto_trait_and_blanket_impls( + cx: &DocContext<'tcx>, + ty: Ty<'tcx>, + param_env_def_id: DefId, +) -> impl Iterator { + AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id).into_iter() + .chain(BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id)) +} + +pub fn register_res(cx: &DocContext<'_>, res: Res) -> DefId { + debug!("register_res({:?})", res); + + let (did, kind) = match res { + Res::Def(DefKind::Fn, i) => (i, TypeKind::Function), + Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef), + Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum), + Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait), + Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct), + Res::Def(DefKind::Union, i) => (i, TypeKind::Union), + Res::Def(DefKind::Mod, i) => (i, TypeKind::Module), + Res::Def(DefKind::ForeignTy, i) => (i, TypeKind::Foreign), + Res::Def(DefKind::Const, i) => (i, TypeKind::Const), + Res::Def(DefKind::Static, i) => (i, TypeKind::Static), + Res::Def(DefKind::Variant, i) => (cx.tcx.parent(i).expect("cannot get parent def id"), + TypeKind::Enum), + Res::Def(DefKind::Macro(mac_kind), i) => match mac_kind { + MacroKind::Bang => (i, TypeKind::Macro), + MacroKind::Attr => (i, TypeKind::Attr), + MacroKind::Derive => (i, TypeKind::Derive), + }, + Res::Def(DefKind::TraitAlias, i) => (i, TypeKind::TraitAlias), + Res::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait), + Res::SelfTy(_, Some(impl_def_id)) => return impl_def_id, + _ => return res.def_id() + }; + if did.is_local() { return did } + inline::record_extern_fqn(cx, did, kind); + if let TypeKind::Trait = kind { + inline::record_extern_trait(cx, did); + } + did +} + +pub fn resolve_use_source(cx: &DocContext<'_>, path: Path) -> ImportSource { + ImportSource { + did: if path.res.opt_def_id().is_none() { + None + } else { + Some(register_res(cx, path.res)) + }, + path, + } +} + +pub fn enter_impl_trait(cx: &DocContext<'_>, f: F) -> R +where + F: FnOnce() -> R, +{ + let old_bounds = mem::take(&mut *cx.impl_trait_bounds.borrow_mut()); + let r = f(); + assert!(cx.impl_trait_bounds.borrow().is_empty()); + *cx.impl_trait_bounds.borrow_mut() = old_bounds; + r +} diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 4cde868201eef..fd620d467de48 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -63,6 +63,13 @@ impl Buffer { } } + crate fn new() -> Buffer { + Buffer { + for_html: false, + buffer: String::new(), + } + } + crate fn is_empty(&self) -> bool { self.buffer.is_empty() } @@ -106,6 +113,10 @@ impl Buffer { write!(self, "{:#}", t); } } + + crate fn is_for_html(&self) -> bool { + self.for_html + } } /// Wrapper struct for properly emitting a function or method declaration. diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index e245764b30209..86e5efbd7b3a4 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2282,12 +2282,23 @@ fn render_implementor(cx: &Context, implementor: &Impl, w: &mut Buffer, fn render_impls(cx: &Context, w: &mut Buffer, traits: &[&&Impl], containing_item: &clean::Item) { - for i in traits { - let did = i.trait_did().unwrap(); - let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods); - render_impl(w, cx, i, assoc_link, - RenderMode::Normal, containing_item.stable_since(), true, None, false, true); - } + let mut impls = traits.iter() + .map(|i| { + let did = i.trait_did().unwrap(); + let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods); + let mut buffer = if w.is_for_html() { + Buffer::html() + } else { + Buffer::new() + }; + render_impl(&mut buffer, cx, i, assoc_link, + RenderMode::Normal, containing_item.stable_since(), + true, None, false, true); + buffer.into_inner() + }) + .collect::>(); + impls.sort(); + w.write_str(&impls.join("")); } fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool) -> String { diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index 522b8b25144f4..e8b9e9cb1f29c 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -433,7 +433,7 @@ mod hash; #[stable(feature = "rust1", since = "1.0.0")] pub mod hash_map { - //! A hash map implemented with linear probing and Robin Hood bucket stealing. + //! A hash map implemented with quadratic probing and SIMD lookup. #[stable(feature = "rust1", since = "1.0.0")] pub use super::hash::map::*; } diff --git a/src/test/ui/consts/assoc_const_generic_impl.rs b/src/test/ui/consts/assoc_const_generic_impl.rs new file mode 100644 index 0000000000000..62702a8ec5cb1 --- /dev/null +++ b/src/test/ui/consts/assoc_const_generic_impl.rs @@ -0,0 +1,19 @@ +#![warn(const_err)] + +trait ZeroSized: Sized { + const I_AM_ZERO_SIZED: (); + fn requires_zero_size(self); +} + +impl ZeroSized for T { + const I_AM_ZERO_SIZED: () = [()][std::mem::size_of::()]; //~ WARN any use of this value + fn requires_zero_size(self) { + let () = Self::I_AM_ZERO_SIZED; //~ ERROR erroneous constant encountered + println!("requires_zero_size called"); + } +} + +fn main() { + ().requires_zero_size(); + 42_u32.requires_zero_size(); +} diff --git a/src/test/ui/consts/assoc_const_generic_impl.stderr b/src/test/ui/consts/assoc_const_generic_impl.stderr new file mode 100644 index 0000000000000..a114d5c6ccd14 --- /dev/null +++ b/src/test/ui/consts/assoc_const_generic_impl.stderr @@ -0,0 +1,22 @@ +warning: any use of this value will cause an error + --> $DIR/assoc_const_generic_impl.rs:9:34 + | +LL | const I_AM_ZERO_SIZED: () = [()][std::mem::size_of::()]; + | -----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | index out of bounds: the len is 1 but the index is 4 + | +note: lint level defined here + --> $DIR/assoc_const_generic_impl.rs:1:9 + | +LL | #![warn(const_err)] + | ^^^^^^^^^ + +error: erroneous constant encountered + --> $DIR/assoc_const_generic_impl.rs:11:18 + | +LL | let () = Self::I_AM_ZERO_SIZED; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error +