diff options
72 files changed, 1688 insertions, 931 deletions
diff --git a/mk/rt.mk b/mk/rt.mk index 590fec53564..65918122801 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -157,6 +157,8 @@ else ifeq ($(findstring android, $(OSTYPE_$(1))), android) # If the test suite passes, however, without symbol prefixes then we should be # good to go! JEMALLOC_ARGS_$(1) := --disable-tls --with-jemalloc-prefix=je_ +else ifeq ($(findstring dragonfly, $(OSTYPE_$(1))), dragonfly) + JEMALLOC_ARGS_$(1) := --with-jemalloc-prefix=je_ endif ifdef CFG_ENABLE_DEBUG_JEMALLOC diff --git a/src/liballoc_jemalloc/build.rs b/src/liballoc_jemalloc/build.rs index 9e2090c3246..5d521913b48 100644 --- a/src/liballoc_jemalloc/build.rs +++ b/src/liballoc_jemalloc/build.rs @@ -86,6 +86,8 @@ fn main() { // should be good to go! cmd.arg("--with-jemalloc-prefix=je_"); cmd.arg("--disable-tls"); + } else if target.contains("dragonfly") { + cmd.arg("--with-jemalloc-prefix=je_"); } if cfg!(feature = "debug-jemalloc") { diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index c96d303e6bb..3a30bebec54 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -42,22 +42,27 @@ use libc::{c_int, c_void, size_t}; extern {} // Note that the symbols here are prefixed by default on OSX (we don't -// explicitly request it), and on Android we explicitly request it as -// unprefixing cause segfaults (mismatches in allocators). +// explicitly request it), and on Android and DragonFly we explicitly request +// it as unprefixing cause segfaults (mismatches in allocators). extern { - #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios"), + #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", + target_os = "dragonfly"), link_name = "je_mallocx")] fn mallocx(size: size_t, flags: c_int) -> *mut c_void; - #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios"), + #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", + target_os = "dragonfly"), link_name = "je_rallocx")] fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void; - #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios"), + #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", + target_os = "dragonfly"), link_name = "je_xallocx")] fn xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t; - #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios"), + #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", + target_os = "dragonfly"), link_name = "je_sdallocx")] fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int); - #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios"), + #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", + target_os = "dragonfly"), link_name = "je_nallocx")] fn nallocx(size: size_t, flags: c_int) -> size_t; } diff --git a/src/liblibc b/src/liblibc -Subproject fb2f0bbdab5aa79d684d1e15ccd755f8d37bc07 +Subproject 7265c17d1845354f979a39b4ceb3a6934025b2a diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 8edf24fbc25..67669c54ac5 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -37,7 +37,7 @@ use super::equate::Equate; use super::glb::Glb; use super::lub::Lub; use super::sub::Sub; -use super::{InferCtxt}; +use super::InferCtxt; use super::{MiscVariable, TypeTrace}; use super::type_variable::{RelationDir, BiTo, EqTo, SubtypeOf, SupertypeOf}; @@ -46,6 +46,7 @@ use ty::{self, Ty, TyCtxt}; use ty::error::TypeError; use ty::fold::{TypeFolder, TypeFoldable}; use ty::relate::{Relate, RelateResult, TypeRelation}; +use traits::PredicateObligations; use syntax::ast; use syntax::codemap::Span; @@ -56,6 +57,7 @@ pub struct CombineFields<'a, 'tcx: 'a> { pub a_is_expected: bool, pub trace: TypeTrace<'tcx>, pub cause: Option<ty::relate::Cause>, + pub obligations: PredicateObligations<'tcx>, } pub fn super_combine_tys<'a,'tcx:'a,R>(infcx: &InferCtxt<'a, 'tcx>, diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index 3c9c9c57884..5540046c9e3 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs @@ -16,6 +16,7 @@ use super::type_variable::{EqTo}; use ty::{self, Ty, TyCtxt}; use ty::TyVar; use ty::relate::{Relate, RelateResult, TypeRelation}; +use traits::PredicateObligations; /// Ensures `a` is made equal to `b`. Returns `a` on success. pub struct Equate<'a, 'tcx: 'a> { @@ -26,6 +27,10 @@ impl<'a, 'tcx> Equate<'a, 'tcx> { pub fn new(fields: CombineFields<'a, 'tcx>) -> Equate<'a, 'tcx> { Equate { fields: fields } } + + pub fn obligations(self) -> PredicateObligations<'tcx> { + self.fields.obligations + } } impl<'a, 'tcx> TypeRelation<'a,'tcx> for Equate<'a, 'tcx> { diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index 235428a6898..37717c2b6bc 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -16,6 +16,7 @@ use super::Subtype; use ty::{self, Ty, TyCtxt}; use ty::relate::{Relate, RelateResult, TypeRelation}; +use traits::PredicateObligations; /// "Greatest lower bound" (common subtype) pub struct Glb<'a, 'tcx: 'a> { @@ -26,6 +27,10 @@ impl<'a, 'tcx> Glb<'a, 'tcx> { pub fn new(fields: CombineFields<'a, 'tcx>) -> Glb<'a, 'tcx> { Glb { fields: fields } } + + pub fn obligations(self) -> PredicateObligations<'tcx> { + self.fields.obligations + } } impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Glb<'a, 'tcx> { diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 00b85929b4b..32b2fe911e8 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -16,6 +16,7 @@ use super::Subtype; use ty::{self, Ty, TyCtxt}; use ty::relate::{Relate, RelateResult, TypeRelation}; +use traits::PredicateObligations; /// "Least upper bound" (common supertype) pub struct Lub<'a, 'tcx: 'a> { @@ -26,6 +27,10 @@ impl<'a, 'tcx> Lub<'a, 'tcx> { pub fn new(fields: CombineFields<'a, 'tcx>) -> Lub<'a, 'tcx> { Lub { fields: fields } } + + pub fn obligations(self) -> PredicateObligations<'tcx> { + self.fields.obligations + } } impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Lub<'a, 'tcx> { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 35e1be7e5f4..e22fb988904 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -27,13 +27,13 @@ use middle::region::CodeExtent; use ty::subst; use ty::subst::Substs; use ty::subst::Subst; -use traits::{self, ProjectionMode}; use ty::adjustment; use ty::{TyVid, IntVid, FloatVid}; use ty::{self, Ty, TyCtxt}; use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use ty::fold::{TypeFolder, TypeFoldable}; use ty::relate::{Relate, RelateResult, TypeRelation}; +use traits::{self, PredicateObligations, ProjectionMode}; use rustc_data_structures::unify::{self, UnificationTable}; use std::cell::{RefCell, Ref}; use std::fmt; @@ -63,6 +63,12 @@ pub mod sub; pub mod type_variable; pub mod unify_key; +pub struct InferOk<'tcx, T> { + pub value: T, + pub obligations: PredicateObligations<'tcx>, +} +pub type InferResult<'tcx, T> = Result<InferOk<'tcx, T>, TypeError<'tcx>>; + pub type Bound<T> = Option<T>; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult<T> = Result<T, FixupError>; // "fixup result" @@ -391,16 +397,15 @@ pub fn mk_subty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, origin: TypeOrigin, a: Ty<'tcx>, b: Ty<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { debug!("mk_subty({:?} <: {:?})", a, b); cx.sub_types(a_is_expected, origin, a, b) } -pub fn can_mk_subty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, - a: Ty<'tcx>, - b: Ty<'tcx>) - -> UnitResult<'tcx> { +pub fn can_mk_subty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) + -> UnitResult<'tcx> +{ debug!("can_mk_subty({:?} <: {:?})", a, b); cx.probe(|_| { let trace = TypeTrace { @@ -412,7 +417,7 @@ pub fn can_mk_subty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, } pub fn can_mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) - -> UnitResult<'tcx> + -> UnitResult<'tcx> { cx.can_equate(&a, &b) } @@ -432,7 +437,7 @@ pub fn mk_eqty<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, origin: TypeOrigin, a: Ty<'tcx>, b: Ty<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { debug!("mk_eqty({:?} <: {:?})", a, b); cx.eq_types(a_is_expected, origin, a, b) @@ -443,7 +448,7 @@ pub fn mk_eq_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, origin: TypeOrigin, a: ty::TraitRef<'tcx>, b: ty::TraitRef<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { debug!("mk_eq_trait_refs({:?} = {:?})", a, b); cx.eq_trait_refs(a_is_expected, origin, a, b) @@ -454,7 +459,7 @@ pub fn mk_sub_poly_trait_refs<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, origin: TypeOrigin, a: ty::PolyTraitRef<'tcx>, b: ty::PolyTraitRef<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { debug!("mk_sub_poly_trait_refs({:?} <: {:?})", a, b); cx.sub_poly_trait_refs(a_is_expected, origin, a, b) @@ -465,7 +470,7 @@ pub fn mk_eq_impl_headers<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, origin: TypeOrigin, a: &ty::ImplHeader<'tcx>, b: &ty::ImplHeader<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { debug!("mk_eq_impl_header({:?} = {:?})", a, b); match (a.trait_ref, b.trait_ref) { @@ -574,6 +579,12 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>, Ok(infcx.tcx.erase_regions(&result)) } +impl<'tcx, T> InferOk<'tcx, T> { + fn unit(self) -> InferOk<'tcx, ()> { + InferOk { value: (), obligations: self.obligations } + } +} + impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn projection_mode(&self) -> ProjectionMode { self.projection_mode @@ -661,39 +672,51 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } fn combine_fields(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>) - -> CombineFields<'a, 'tcx> { - CombineFields {infcx: self, - a_is_expected: a_is_expected, - trace: trace, - cause: None} + -> CombineFields<'a, 'tcx> + { + CombineFields { + infcx: self, + a_is_expected: a_is_expected, + trace: trace, + cause: None, + obligations: PredicateObligations::new(), + } } pub fn equate<T>(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>, a: &T, b: &T) - -> RelateResult<'tcx, T> + -> InferResult<'tcx, T> where T: Relate<'a, 'tcx> { - self.combine_fields(a_is_expected, trace).equate().relate(a, b) + let mut equate = self.combine_fields(a_is_expected, trace).equate(); + let result = equate.relate(a, b); + result.map(|t| InferOk { value: t, obligations: equate.obligations() }) } pub fn sub<T>(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>, a: &T, b: &T) - -> RelateResult<'tcx, T> + -> InferResult<'tcx, T> where T: Relate<'a, 'tcx> { - self.combine_fields(a_is_expected, trace).sub().relate(a, b) + let mut sub = self.combine_fields(a_is_expected, trace).sub(); + let result = sub.relate(a, b); + result.map(|t| InferOk { value: t, obligations: sub.obligations() }) } pub fn lub<T>(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>, a: &T, b: &T) - -> RelateResult<'tcx, T> + -> InferResult<'tcx, T> where T: Relate<'a, 'tcx> { - self.combine_fields(a_is_expected, trace).lub().relate(a, b) + let mut lub = self.combine_fields(a_is_expected, trace).lub(); + let result = lub.relate(a, b); + result.map(|t| InferOk { value: t, obligations: lub.obligations() }) } pub fn glb<T>(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>, a: &T, b: &T) - -> RelateResult<'tcx, T> + -> InferResult<'tcx, T> where T: Relate<'a, 'tcx> { - self.combine_fields(a_is_expected, trace).glb().relate(a, b) + let mut glb = self.combine_fields(a_is_expected, trace).glb(); + let result = glb.relate(a, b); + result.map(|t| InferOk { value: t, obligations: glb.obligations() }) } fn start_snapshot(&self) -> CombinedSnapshot { @@ -829,12 +852,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: TypeOrigin, a: Ty<'tcx>, b: Ty<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { debug!("sub_types({:?} <: {:?})", a, b); self.commit_if_ok(|_| { let trace = TypeTrace::types(origin, a_is_expected, a, b); - self.sub(a_is_expected, trace, &a, &b).map(|_| ()) + self.sub(a_is_expected, trace, &a, &b).map(|ok| ok.unit()) }) } @@ -843,11 +866,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: TypeOrigin, a: Ty<'tcx>, b: Ty<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { self.commit_if_ok(|_| { let trace = TypeTrace::types(origin, a_is_expected, a, b); - self.equate(a_is_expected, trace, &a, &b).map(|_| ()) + self.equate(a_is_expected, trace, &a, &b).map(|ok| ok.unit()) }) } @@ -856,7 +879,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: TypeOrigin, a: ty::TraitRef<'tcx>, b: ty::TraitRef<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { debug!("eq_trait_refs({:?} <: {:?})", a, @@ -866,7 +889,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: origin, values: TraitRefs(expected_found(a_is_expected, a.clone(), b.clone())) }; - self.equate(a_is_expected, trace, &a, &b).map(|_| ()) + self.equate(a_is_expected, trace, &a, &b).map(|ok| ok.unit()) }) } @@ -875,7 +898,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: TypeOrigin, a: ty::PolyTraitRef<'tcx>, b: ty::PolyTraitRef<'tcx>) - -> UnitResult<'tcx> + -> InferResult<'tcx, ()> { debug!("sub_poly_trait_refs({:?} <: {:?})", a, @@ -885,7 +908,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: origin, values: PolyTraitRefs(expected_found(a_is_expected, a.clone(), b.clone())) }; - self.sub(a_is_expected, trace, &a, &b).map(|_| ()) + self.sub(a_is_expected, trace, &a, &b).map(|ok| ok.unit()) }) } @@ -928,20 +951,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn equality_predicate(&self, span: Span, predicate: &ty::PolyEquatePredicate<'tcx>) - -> UnitResult<'tcx> { + -> InferResult<'tcx, ()> + { self.commit_if_ok(|snapshot| { let (ty::EquatePredicate(a, b), skol_map) = self.skolemize_late_bound_regions(predicate, snapshot); let origin = TypeOrigin::EquatePredicate(span); - let () = mk_eqty(self, false, origin, a, b)?; - self.leak_check(&skol_map, snapshot) + let eqty_ok = mk_eqty(self, false, origin, a, b)?; + self.leak_check(&skol_map, snapshot).map(|_| eqty_ok.unit()) }) } pub fn region_outlives_predicate(&self, span: Span, predicate: &ty::PolyRegionOutlivesPredicate) - -> UnitResult<'tcx> { + -> UnitResult<'tcx> + { self.commit_if_ok(|snapshot| { let (ty::OutlivesPredicate(r_a, r_b), skol_map) = self.skolemize_late_bound_regions(predicate, snapshot); diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index 0505c9d627b..ece8c0c696a 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -16,6 +16,7 @@ use super::type_variable::{SubtypeOf, SupertypeOf}; use ty::{self, Ty, TyCtxt}; use ty::TyVar; use ty::relate::{Cause, Relate, RelateResult, TypeRelation}; +use traits::PredicateObligations; use std::mem; /// Ensures `a` is made a subtype of `b`. Returns `a` on success. @@ -27,6 +28,10 @@ impl<'a, 'tcx> Sub<'a, 'tcx> { pub fn new(f: CombineFields<'a, 'tcx>) -> Sub<'a, 'tcx> { Sub { fields: f } } + + pub fn obligations(self) -> PredicateObligations<'tcx> { + self.fields.obligations + } } impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> { diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 879b8945620..1573d0c4292 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -179,6 +179,12 @@ declare_lint! { "lints that have been renamed or removed" } +declare_lint! { + pub SUPER_OR_SELF_IN_GLOBAL_PATH, + Warn, + "detects super or self keywords at the beginning of global path" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -213,7 +219,8 @@ impl LintPass for HardwiredLints { RAW_POINTER_DERIVE, TRANSMUTE_FROM_FN_ITEM_TYPES, OVERLAPPING_INHERENT_IMPLS, - RENAMED_AND_REMOVED_LINTS + RENAMED_AND_REMOVED_LINTS, + SUPER_OR_SELF_IN_GLOBAL_PATH ) } } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index cc37ee7dbda..4451e7ac472 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1502,7 +1502,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } else { let ends_with_stmt = match body.expr { None if !body.stmts.is_empty() => - match body.stmts.first().unwrap().node { + match body.stmts.last().unwrap().node { hir::StmtSemi(ref e, _) => { self.ir.tcx.expr_ty(&e) == t_ret }, @@ -1515,7 +1515,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { E0269, "not all control paths return a value"); if ends_with_stmt { - let last_stmt = body.stmts.first().unwrap(); + let last_stmt = body.stmts.last().unwrap(); let original_span = original_sp(self.ir.tcx.sess.codemap(), last_stmt.span, sp); let span_semicolon = Span { diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 321144126c9..11e8dae8717 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -9,7 +9,7 @@ // except according to those terms. use dep_graph::DepGraph; -use infer::InferCtxt; +use infer::{InferCtxt, InferOk}; use ty::{self, Ty, TyCtxt, TypeFoldable, ToPolyTraitRef}; use rustc_data_structures::obligation_forest::{Backtrace, ObligationForest, Error}; use std::iter; @@ -320,103 +320,172 @@ impl<'tcx> FulfillmentContext<'tcx> { fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, tree_cache: &mut LocalFulfilledPredicates<'tcx>, pending_obligation: &mut PendingPredicateObligation<'tcx>, - mut backtrace: Backtrace<PendingPredicateObligation<'tcx>>, + backtrace: Backtrace<PendingPredicateObligation<'tcx>>, region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>) -> Result<Option<Vec<PendingPredicateObligation<'tcx>>>, FulfillmentErrorCode<'tcx>> { - match process_predicate1(selcx, pending_obligation, backtrace.clone(), region_obligations) { - Ok(Some(v)) => { - // FIXME(#30977) The code below is designed to detect (and - // permit) DAGs, while still ensuring that the reasoning - // is acyclic. However, it does a few things - // suboptimally. For example, it refreshes type variables - // a lot, probably more than needed, but also less than - // you might want. - // - // - more than needed: I want to be very sure we don't - // accidentally treat a cycle as a DAG, so I am - // refreshing type variables as we walk the ancestors; - // but we are going to repeat this a lot, which is - // sort of silly, and it would be nicer to refresh - // them *in place* so that later predicate processing - // can benefit from the same work; - // - less than you might want: we only add items in the cache here, - // but maybe we learn more about type variables and could add them into - // the cache later on. - - let tcx = selcx.tcx(); - - // Compute a little FnvHashSet for the ancestors. We only - // do this the first time that we care. - let mut cache = None; - let mut is_ancestor = |predicate: &ty::Predicate<'tcx>| { - if cache.is_none() { - let mut c = FnvHashSet(); - for ancestor in backtrace.by_ref() { - // Ugh. This just feels ridiculously - // inefficient. But we need to compare - // predicates without being concerned about - // the vagaries of type inference, so for now - // just ensure that they are always - // up-to-date. (I suppose we could just use a - // snapshot and check if they are unifiable?) - let resolved_predicate = - selcx.infcx().resolve_type_vars_if_possible( - &ancestor.obligation.predicate); - c.insert(resolved_predicate); - } - cache = Some(c); + match process_predicate1(selcx, pending_obligation, region_obligations) { + Ok(Some(v)) => process_child_obligations(selcx, + tree_cache, + &pending_obligation.obligation, + backtrace, + v), + Ok(None) => Ok(None), + Err(e) => Err(e) + } +} + +fn process_child_obligations<'a,'tcx>( + selcx: &mut SelectionContext<'a,'tcx>, + tree_cache: &mut LocalFulfilledPredicates<'tcx>, + pending_obligation: &PredicateObligation<'tcx>, + backtrace: Backtrace<PendingPredicateObligation<'tcx>>, + child_obligations: Vec<PredicateObligation<'tcx>>) + -> Result<Option<Vec<PendingPredicateObligation<'tcx>>>, + FulfillmentErrorCode<'tcx>> +{ + // FIXME(#30977) The code below is designed to detect (and + // permit) DAGs, while still ensuring that the reasoning + // is acyclic. However, it does a few things + // suboptimally. For example, it refreshes type variables + // a lot, probably more than needed, but also less than + // you might want. + // + // - more than needed: I want to be very sure we don't + // accidentally treat a cycle as a DAG, so I am + // refreshing type variables as we walk the ancestors; + // but we are going to repeat this a lot, which is + // sort of silly, and it would be nicer to refresh + // them *in place* so that later predicate processing + // can benefit from the same work; + // - less than you might want: we only add items in the cache here, + // but maybe we learn more about type variables and could add them into + // the cache later on. + + let tcx = selcx.tcx(); + + let mut ancestor_set = AncestorSet::new(&backtrace); + + let pending_predicate_obligations: Vec<_> = + child_obligations + .into_iter() + .filter_map(|obligation| { + // Probably silly, but remove any inference + // variables. This is actually crucial to the ancestor + // check marked (*) below, but it's not clear that it + // makes sense to ALWAYS do it. + let obligation = selcx.infcx().resolve_type_vars_if_possible(&obligation); + + // Screen out obligations that we know globally + // are true. + if tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate) { + return None; + } + + // Check whether this obligation appears + // somewhere else in the tree. If not, we have to + // process it for sure. + if !tree_cache.is_duplicate_or_add(&obligation.predicate) { + return Some(PendingPredicateObligation { + obligation: obligation, + stalled_on: vec![] + }); + } + + debug!("process_child_obligations: duplicate={:?}", + obligation.predicate); + + // OK, the obligation appears elsewhere in the tree. + // This is either a fatal error or else something we can + // ignore. If the obligation appears in our *ancestors* + // (rather than some more distant relative), that + // indicates a cycle. Cycles are either considered + // resolved (if this is a coinductive case) or a fatal + // error. + if let Some(index) = ancestor_set.has(selcx.infcx(), &obligation.predicate) { + // ~~~ (*) see above + debug!("process_child_obligations: cycle index = {}", index); + + let backtrace = backtrace.clone(); + let cycle: Vec<_> = + iter::once(&obligation) + .chain(Some(pending_obligation)) + .chain(backtrace.take(index + 1).map(|p| &p.obligation)) + .cloned() + .collect(); + if coinductive_match(selcx, &cycle) { + debug!("process_child_obligations: coinductive match"); + None + } else { + report_overflow_error_cycle(selcx.infcx(), &cycle); } + } else { + // Not a cycle. Just ignore this obligation then, + // we're already in the process of proving it. + debug!("process_child_obligations: not a cycle"); + None + } + }) + .collect(); - cache.as_ref().unwrap().contains(predicate) - }; + Ok(Some(pending_predicate_obligations)) +} + +struct AncestorSet<'b, 'tcx: 'b> { + populated: bool, + cache: FnvHashMap<ty::Predicate<'tcx>, usize>, + backtrace: Backtrace<'b, PendingPredicateObligation<'tcx>>, +} - let pending_predicate_obligations: Vec<_> = - v.into_iter() - .filter_map(|obligation| { - // Probably silly, but remove any inference - // variables. This is actually crucial to the - // ancestor check below, but it's not clear that - // it makes sense to ALWAYS do it. - let obligation = selcx.infcx().resolve_type_vars_if_possible(&obligation); - - // Screen out obligations that we know globally - // are true. This should really be the DAG check - // mentioned above. - if tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate) { - return None; - } - - // Check whether this obligation appears somewhere else in the tree. - if tree_cache.is_duplicate_or_add(&obligation.predicate) { - // If the obligation appears as a parent, - // allow it, because that is a cycle. - // Otherwise though we can just ignore - // it. Note that we have to be careful around - // inference variables here -- for the - // purposes of the ancestor check, we retain - // the invariant that all type variables are - // fully refreshed. - if !is_ancestor(&obligation.predicate) { - return None; - } - } - - Some(PendingPredicateObligation { - obligation: obligation, - stalled_on: vec![] - }) - }) - .collect(); - - Ok(Some(pending_predicate_obligations)) +impl<'b, 'tcx> AncestorSet<'b, 'tcx> { + fn new(backtrace: &Backtrace<'b, PendingPredicateObligation<'tcx>>) -> Self { + AncestorSet { + populated: false, + cache: FnvHashMap(), + backtrace: backtrace.clone(), } - Ok(None) => Ok(None), - Err(e) => Err(e) } -} + /// Checks whether any of the ancestors in the backtrace are equal + /// to `predicate` (`predicate` is assumed to be fully + /// type-resolved). Returns `None` if not; otherwise, returns + /// `Some` with the index within the backtrace. + fn has<'a>(&mut self, + infcx: &InferCtxt<'a, 'tcx>, + predicate: &ty::Predicate<'tcx>) + -> Option<usize> { + // the first time, we have to populate the cache + if !self.populated { + let backtrace = self.backtrace.clone(); + for (index, ancestor) in backtrace.enumerate() { + // Ugh. This just feels ridiculously + // inefficient. But we need to compare + // predicates without being concerned about + // the vagaries of type inference, so for now + // just ensure that they are always + // up-to-date. (I suppose we could just use a + // snapshot and check if they are unifiable?) + let resolved_predicate = + infcx.resolve_type_vars_if_possible( + &ancestor.obligation.predicate); + + // Though we try to avoid it, it can happen that a + // cycle already exists in the predecessors. This + // happens if the type variables were not fully known + // at the time that the ancestors were pushed. We'll + // just ignore such cycles for now, on the premise + // that they will repeat themselves and we'll deal + // with them properly then. + self.cache.entry(resolved_predicate) + .or_insert(index); + } + self.populated = true; + } + + self.cache.get(predicate).cloned() + } +} /// Return the set of type variables contained in a trait ref fn trait_ref_type_vars<'a, 'tcx>(selcx: &mut SelectionContext<'a, 'tcx>, @@ -438,7 +507,6 @@ fn trait_ref_type_vars<'a, 'tcx>(selcx: &mut SelectionContext<'a, 'tcx>, /// - `Err` if the predicate does not hold fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, pending_obligation: &mut PendingPredicateObligation<'tcx>, - backtrace: Backtrace<PendingPredicateObligation<'tcx>>, region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>) -> Result<Option<Vec<PredicateObligation<'tcx>>>, FulfillmentErrorCode<'tcx>> @@ -461,16 +529,6 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, let obligation = &mut pending_obligation.obligation; - // If we exceed the recursion limit, take a moment to look for a - // cycle so we can give a better error report from here, where we - // have more context. - let recursion_limit = selcx.tcx().sess.recursion_limit.get(); - if obligation.recursion_depth >= recursion_limit { - if let Some(cycle) = scan_for_cycle(obligation, &backtrace) { - report_overflow_error_cycle(selcx.infcx(), &cycle); - } - } - if obligation.predicate.has_infer_types() { obligation.predicate = selcx.infcx().resolve_type_vars_if_possible(&obligation.predicate); } @@ -481,10 +539,6 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, return Ok(Some(vec![])); } - if coinductive_match(selcx, obligation, data, &backtrace) { - return Ok(Some(vec![])); - } - let trait_obligation = obligation.with(data.clone()); match selcx.select(&trait_obligation) { Ok(Some(vtable)) => { @@ -526,7 +580,11 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, ty::Predicate::Equate(ref binder) => { match selcx.infcx().equality_predicate(obligation.cause.span, binder) { - Ok(()) => Ok(Some(Vec::new())), + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + Ok(Some(Vec::new())) + }, Err(_) => Err(CodeSelectionError(Unimplemented)), } } @@ -609,63 +667,40 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, /// For defaulted traits, we use a co-inductive strategy to solve, so /// that recursion is ok. This routine returns true if the top of the -/// stack (`top_obligation` and `top_data`): +/// stack (`cycle[0]`): /// - is a defaulted trait, and /// - it also appears in the backtrace at some position `X`; and, /// - all the predicates at positions `X..` between `X` an the top are /// also defaulted traits. fn coinductive_match<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, - top_obligation: &PredicateObligation<'tcx>, - top_data: &ty::PolyTraitPredicate<'tcx>, - backtrace: &Backtrace<PendingPredicateObligation<'tcx>>) + cycle: &[PredicateObligation<'tcx>]) -> bool { - if selcx.tcx().trait_has_default_impl(top_data.def_id()) { - debug!("coinductive_match: top_data={:?}", top_data); - for bt_obligation in backtrace.clone() { - debug!("coinductive_match: bt_obligation={:?}", bt_obligation); - - // *Everything* in the backtrace must be a defaulted trait. - match bt_obligation.obligation.predicate { - ty::Predicate::Trait(ref data) => { - if !selcx.tcx().trait_has_default_impl(data.def_id()) { - debug!("coinductive_match: trait does not have default impl"); - break; - } - } - _ => { break; } - } - - // And we must find a recursive match. - if bt_obligation.obligation.predicate == top_obligation.predicate { - debug!("coinductive_match: found a match in the backtrace"); - return true; - } - } - } - - false + let len = cycle.len(); + + assert_eq!(cycle[0].predicate, cycle[len - 1].predicate); + + cycle[0..len-1] + .iter() + .all(|bt_obligation| { + let result = coinductive_obligation(selcx, bt_obligation); + debug!("coinductive_match: bt_obligation={:?} coinductive={}", + bt_obligation, result); + result + }) } -fn scan_for_cycle<'a,'tcx>(top_obligation: &PredicateObligation<'tcx>, - backtrace: &Backtrace<PendingPredicateObligation<'tcx>>) - -> Option<Vec<PredicateObligation<'tcx>>> -{ - let mut map = FnvHashMap(); - let all_obligations = - || iter::once(top_obligation) - .chain(backtrace.clone() - .map(|p| &p.obligation)); - for (index, bt_obligation) in all_obligations().enumerate() { - if let Some(&start) = map.get(&bt_obligation.predicate) { - // Found a cycle starting at position `start` and running - // until the current position (`index`). - return Some(all_obligations().skip(start).take(index - start + 1).cloned().collect()); - } else { - map.insert(bt_obligation.predicate.clone(), index); +fn coinductive_obligation<'a, 'tcx>(selcx: &SelectionContext<'a, 'tcx>, + obligation: &PredicateObligation<'tcx>) + -> bool { + match obligation.predicate { + ty::Predicate::Trait(ref data) => { + selcx.tcx().trait_has_default_impl(data.def_id()) + } + _ => { + false } } - None } fn register_region_obligation<'tcx>(t_a: Ty<'tcx>, diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index f7b75c2259e..d4d61ec0244 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -24,7 +24,7 @@ use super::VtableImplData; use super::util; use middle::def_id::DefId; -use infer::{self, TypeOrigin}; +use infer::{self, InferOk, TypeOrigin}; use ty::subst::Subst; use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder}; @@ -232,7 +232,11 @@ fn project_and_unify_type<'cx,'tcx>( let infcx = selcx.infcx(); let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span); match infer::mk_eqty(infcx, true, origin, normalized_ty, obligation.predicate.ty) { - Ok(()) => Ok(Some(obligations)), + Ok(InferOk { obligations: inferred_obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(inferred_obligations.is_empty()); + Ok(Some(obligations)) + }, Err(err) => Err(MismatchedProjectionTypes { err: err }), } } @@ -278,7 +282,10 @@ fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span); let obligation_ty = obligation.predicate.ty; match infer::mk_eqty(infcx, true, origin, obligation_ty, ret_type) { - Ok(()) => { } + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + } Err(_) => { /* ignore errors */ } } } @@ -829,7 +836,10 @@ fn assemble_candidates_from_predicates<'cx,'tcx,I>( infcx.sub_poly_trait_refs(false, origin, data_poly_trait_ref, - obligation_poly_trait_ref).is_ok() + obligation_poly_trait_ref) + // FIXME(#32730) propagate obligations + .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) + .is_ok() }); debug!("assemble_candidates_from_predicates: candidate={:?} \ @@ -1082,7 +1092,10 @@ fn confirm_param_env_candidate<'cx,'tcx>( origin, obligation.predicate.trait_ref.clone(), projection.projection_ty.trait_ref.clone()) { - Ok(()) => { } + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + } Err(e) => { span_bug!( obligation.cause.span, diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 7635ff1eb4c..f68386feddb 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -38,7 +38,7 @@ use super::util; use middle::def_id::DefId; use infer; -use infer::{InferCtxt, TypeFreshener, TypeOrigin}; +use infer::{InferCtxt, InferOk, TypeFreshener, TypeOrigin}; use ty::subst::{Subst, Substs, TypeSpace}; use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; use traits; @@ -484,7 +484,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Predicate::Equate(ref p) => { // does this code ever run? match self.infcx.equality_predicate(obligation.cause.span, p) { - Ok(()) => EvaluatedToOk, + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + EvaluatedToOk + }, Err(_) => EvaluatedToErr } } @@ -1185,7 +1189,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { origin, trait_bound.clone(), ty::Binder(skol_trait_ref.clone())) { - Ok(()) => { } + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + } Err(_) => { return false; } } @@ -2487,13 +2494,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let origin = TypeOrigin::RelateOutputImplTypes(obligation_cause.span); let obligation_trait_ref = obligation_trait_ref.clone(); - match self.infcx.sub_poly_trait_refs(false, - origin, - expected_trait_ref.clone(), - obligation_trait_ref.clone()) { - Ok(()) => Ok(()), - Err(e) => Err(OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) - } + self.infcx.sub_poly_trait_refs(false, + origin, + expected_trait_ref.clone(), + obligation_trait_ref.clone()) + // FIXME(#32730) propagate obligations + .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) + .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) } fn confirm_builtin_unsize_candidate(&mut self, @@ -2524,9 +2531,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let new_trait = tcx.mk_trait(data_a.principal.clone(), bounds); let origin = TypeOrigin::Misc(obligation.cause.span); - if self.infcx.sub_types(false, origin, new_trait, target).is_err() { - return Err(Unimplemented); - } + let InferOk { obligations, .. } = + self.infcx.sub_types(false, origin, new_trait, target) + .map_err(|_| Unimplemented)?; + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); // Register one obligation for 'a: 'b. let cause = ObligationCause::new(obligation.cause.span, @@ -2589,9 +2598,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // [T; n] -> [T]. (&ty::TyArray(a, _), &ty::TySlice(b)) => { let origin = TypeOrigin::Misc(obligation.cause.span); - if self.infcx.sub_types(false, origin, a, b).is_err() { - return Err(Unimplemented); - } + let InferOk { obligations, .. } = + self.infcx.sub_types(false, origin, a, b) + .map_err(|_| Unimplemented)?; + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); } // Struct<T> -> Struct<U>. @@ -2647,9 +2658,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let new_struct = tcx.mk_struct(def, tcx.mk_substs(new_substs)); let origin = TypeOrigin::Misc(obligation.cause.span); - if self.infcx.sub_types(false, origin, new_struct, target).is_err() { - return Err(Unimplemented); - } + let InferOk { obligations, .. } = + self.infcx.sub_types(false, origin, new_struct, target) + .map_err(|_| Unimplemented)?; + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); // Construct the nested Field<T>: Unsize<Field<U>> predicate. nested.push(util::predicate_for_trait_def(tcx, @@ -2734,13 +2747,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { skol_obligation_trait_ref); let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span); - if let Err(e) = self.infcx.eq_trait_refs(false, - origin, - impl_trait_ref.value.clone(), - skol_obligation_trait_ref) { - debug!("match_impl: failed eq_trait_refs due to `{}`", e); - return Err(()); - } + let InferOk { obligations, .. } = + self.infcx.eq_trait_refs(false, + origin, + impl_trait_ref.value.clone(), + skol_obligation_trait_ref) + .map_err(|e| { + debug!("match_impl: failed eq_trait_refs due to `{}`", e); + () + })?; + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); if let Err(e) = self.infcx.leak_check(&skol_map, snapshot) { debug!("match_impl: failed leak check due to `{}`", e); @@ -2803,13 +2820,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { poly_trait_ref); let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span); - match self.infcx.sub_poly_trait_refs(false, - origin, - poly_trait_ref, - obligation.predicate.to_poly_trait_ref()) { - Ok(()) => Ok(()), - Err(_) => Err(()), - } + self.infcx.sub_poly_trait_refs(false, + origin, + poly_trait_ref, + obligation.predicate.to_poly_trait_ref()) + // FIXME(#32730) propagate obligations + .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) + .map_err(|_| ()) } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index d04f272688c..54223e16e17 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -116,21 +116,9 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { pub trait TypeFolder<'tcx> : Sized { fn tcx<'a>(&'a self) -> &'a TyCtxt<'tcx>; - /// Invoked by the `super_*` routines when we enter a region - /// binding level (for example, when entering a function - /// signature). This is used by clients that want to track the - /// Debruijn index nesting level. - fn enter_region_binder(&mut self) { } - - /// Invoked by the `super_*` routines when we exit a region - /// binding level. This is used by clients that want to - /// track the Debruijn index nesting level. - fn exit_region_binder(&mut self) { } - fn fold_binder<T>(&mut self, t: &Binder<T>) -> Binder<T> where T : TypeFoldable<'tcx> { - // FIXME(#20526) this should replace `enter_region_binder`/`exit_region_binder`. t.super_fold_with(self) } @@ -197,8 +185,9 @@ pub trait TypeFolder<'tcx> : Sized { } pub trait TypeVisitor<'tcx> : Sized { - fn enter_region_binder(&mut self) { } - fn exit_region_binder(&mut self) { } + fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> bool { + t.super_visit_with(self) + } fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { t.super_visit_with(self) @@ -296,12 +285,11 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> { fn tcx(&self) -> &TyCtxt<'tcx> { self.tcx } - fn enter_region_binder(&mut self) { + fn fold_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T> { self.current_depth += 1; - } - - fn exit_region_binder(&mut self) { + let t = t.super_fold_with(self); self.current_depth -= 1; + t } fn fold_region(&mut self, r: ty::Region) -> ty::Region { @@ -438,12 +426,11 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx> { fn tcx(&self) -> &TyCtxt<'tcx> { self.tcx } - fn enter_region_binder(&mut self) { + fn fold_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T> { self.current_depth += 1; - } - - fn exit_region_binder(&mut self) { + let t = t.super_fold_with(self); self.current_depth -= 1; + t } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { @@ -596,12 +583,11 @@ struct HasEscapingRegionsVisitor { } impl<'tcx> TypeVisitor<'tcx> for HasEscapingRegionsVisitor { - fn enter_region_binder(&mut self) { + fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> bool { self.depth += 1; - } - - fn exit_region_binder(&mut self) { + let result = t.super_visit_with(self); self.depth -= 1; + result } fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 6fca1eef701..82cc9e7f2e3 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -190,10 +190,7 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec<T> { impl<'tcx, T:TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<T> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - folder.enter_region_binder(); - let result = ty::Binder(self.0.fold_with(folder)); - folder.exit_region_binder(); - result + ty::Binder(self.0.fold_with(folder)) } fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { @@ -201,10 +198,11 @@ impl<'tcx, T:TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<T> { } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { - visitor.enter_region_binder(); - if self.0.visit_with(visitor) { return true } - visitor.exit_region_binder(); - false + self.0.visit_with(visitor) + } + + fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { + visitor.visit_binder(self) } } @@ -220,39 +218,11 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for P<[T]> { impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for VecPerParamSpace<T> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - - // Things in the Fn space take place under an additional level - // of region binding relative to the other spaces. This is - // because those entries are attached to a method, and methods - // always introduce a level of region binding. - - let result = self.map_enumerated(|(space, index, elem)| { - if space == subst::FnSpace && index == 0 { - // enter new level when/if we reach the first thing in fn space - folder.enter_region_binder(); - } - elem.fold_with(folder) - }); - if result.len(subst::FnSpace) > 0 { - // if there was anything in fn space, exit the region binding level - folder.exit_region_binder(); - } - result + self.map(|elem| elem.fold_with(folder)) } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { - let mut entered_region_binder = false; - let result = self.iter_enumerated().any(|(space, index, t)| { - if space == subst::FnSpace && index == 0 { - visitor.enter_region_binder(); - entered_region_binder = true; - } - t.visit_with(visitor) - }); - if entered_region_binder { - visitor.exit_region_binder(); - } - result + self.iter().any(|elem| elem.visit_with(visitor)) } } diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index 5b05c632a97..93157f28482 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -582,12 +582,11 @@ struct SubstFolder<'a, 'tcx: 'a> { impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { fn tcx(&self) -> &TyCtxt<'tcx> { self.tcx } - fn enter_region_binder(&mut self) { + fn fold_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T> { self.region_binders_passed += 1; - } - - fn exit_region_binder(&mut self) { + let t = t.super_fold_with(self); self.region_binders_passed -= 1; + t } fn fold_region(&mut self, r: ty::Region) -> ty::Region { diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 4790e481937..5a00b4573de 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -343,10 +343,15 @@ pub fn eval_const_expr(tcx: &TyCtxt, e: &Expr) -> ConstVal { match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) { Ok(r) => r, // non-const path still needs to be a fatal error, because enums are funky - Err(ref s) if s.kind == NonConstPath => tcx.sess.span_fatal(s.span, &s.description()), Err(s) => { - tcx.sess.span_err(s.span, &s.description()); - Dummy + match s.kind { + NonConstPath | + UnimplementedConstVal(_) => tcx.sess.span_fatal(s.span, &s.description()), + _ => { + tcx.sess.span_err(s.span, &s.description()); + Dummy + } + } }, } } @@ -607,6 +612,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, const_val => signal!(e, NotOn(const_val)), } } + hir::ExprUnary(hir::UnDeref, _) => signal!(e, UnimplementedConstVal("deref operation")), hir::ExprBinary(op, ref a, ref b) => { let b_ty = match op.node { hir::BiShl | hir::BiShr => ty_hint.erase_hint(), @@ -745,7 +751,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, if let Some(const_expr) = lookup_variant_by_id(tcx, enum_def, variant_def) { eval_const_expr_partial(tcx, const_expr, ty_hint, None)? } else { - signal!(e, NonConstPath); + signal!(e, UnimplementedConstVal("enum variants")); } } Def::Struct(..) => { @@ -768,6 +774,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, let callee_val = eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)?; let did = match callee_val { Function(did) => did, + Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")), callee => signal!(e, CallOn(callee)), }; let (decl, result) = if let Some(fn_like) = lookup_const_fn_by_id(tcx, did) { @@ -798,7 +805,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, hir::ExprBlock(ref block) => { match block.expr { Some(ref expr) => eval_const_expr_partial(tcx, &expr, ty_hint, fn_args)?, - None => bug!(), + None => signal!(e, UnimplementedConstVal("empty block")), } } hir::ExprType(ref e, _) => eval_const_expr_partial(tcx, &e, ty_hint, fn_args)?, @@ -840,7 +847,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, }, Str(ref s) if idx as usize >= s.len() => signal!(e, IndexOutOfBounds), - Str(_) => bug!("unimplemented"), // FIXME: return a const char + // FIXME: return a const char + Str(_) => signal!(e, UnimplementedConstVal("indexing into str")), _ => signal!(e, IndexedNonVec), } } @@ -894,6 +902,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, signal!(base, ExpectedConstStruct); } } + hir::ExprAddrOf(..) => signal!(e, UnimplementedConstVal("address operator")), _ => signal!(e, MiscCatchAll) }; @@ -1073,6 +1082,7 @@ fn cast_const_int<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstInt, ty: ty::Ty) -> CastRe Ok(Float(val as f64)) }, ty::TyFloat(ast::FloatTy::F32) => Ok(Float(val.to_u64().unwrap() as f32 as f64)), + ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")), _ => Err(CannotCast), } } @@ -1094,6 +1104,7 @@ fn cast_const<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstVal, ty: ty::Ty) -> CastResult Bool(b) => cast_const_int(tcx, Infer(b as u64), ty), Float(f) => cast_const_float(tcx, f, ty), Char(c) => cast_const_int(tcx, Infer(c as u64), ty), + Function(_) => Err(UnimplementedConstVal("casting fn pointers")), _ => Err(CannotCast), } } diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 6ce623a3b28..ce0d42203b9 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -24,8 +24,8 @@ use rustc::ty::subst; use rustc::ty::subst::Subst; use rustc::traits::ProjectionMode; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::relate::{TypeRelation, RelateResult}; -use rustc::infer::{self, TypeOrigin}; +use rustc::ty::relate::TypeRelation; +use rustc::infer::{self, InferOk, InferResult, TypeOrigin}; use rustc_metadata::cstore::CStore; use rustc::front::map as hir_map; use rustc::session::{self, config}; @@ -355,17 +355,17 @@ impl<'a, 'tcx> Env<'a, 'tcx> { infer::TypeTrace::dummy(self.tcx()) } - pub fn sub(&self, t1: &Ty<'tcx>, t2: &Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + pub fn sub(&self, t1: &Ty<'tcx>, t2: &Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { let trace = self.dummy_type_trace(); self.infcx.sub(true, trace, t1, t2) } - pub fn lub(&self, t1: &Ty<'tcx>, t2: &Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + pub fn lub(&self, t1: &Ty<'tcx>, t2: &Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { let trace = self.dummy_type_trace(); self.infcx.lub(true, trace, t1, t2) } - pub fn glb(&self, t1: &Ty<'tcx>, t2: &Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + pub fn glb(&self, t1: &Ty<'tcx>, t2: &Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { let trace = self.dummy_type_trace(); self.infcx.glb(true, trace, t1, t2) } @@ -374,7 +374,10 @@ impl<'a, 'tcx> Env<'a, 'tcx> { /// region checks). pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) { match self.sub(&t1, &t2) { - Ok(_) => {} + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) once obligations are being propagated, assert the right thing. + assert!(obligations.is_empty()); + } Err(ref e) => { panic!("unexpected error computing sub({:?},{:?}): {}", t1, t2, e); } @@ -395,7 +398,10 @@ impl<'a, 'tcx> Env<'a, 'tcx> { /// Checks that `LUB(t1,t2) == t_lub` pub fn check_lub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>, t_lub: Ty<'tcx>) { match self.lub(&t1, &t2) { - Ok(t) => { + Ok(InferOk { obligations, value: t }) => { + // FIXME(#32730) once obligations are being propagated, assert the right thing. + assert!(obligations.is_empty()); + self.assert_eq(t, t_lub); } Err(ref e) => { @@ -411,7 +417,10 @@ impl<'a, 'tcx> Env<'a, 'tcx> { Err(e) => { panic!("unexpected error computing LUB: {:?}", e) } - Ok(t) => { + Ok(InferOk { obligations, value: t }) => { + // FIXME(#32730) once obligations are being propagated, assert the right thing. + assert!(obligations.is_empty()); + self.assert_eq(t, t_glb); // sanity check for good measure: diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 6e3a961caca..0780e4cd048 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -168,6 +168,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { reference: "PR 30742 <https://github.com/rust-lang/rust/pull/30724>", }, FutureIncompatibleInfo { + id: LintId::of(SUPER_OR_SELF_IN_GLOBAL_PATH), + reference: "PR #32403 <https://github.com/rust-lang/rust/pull/32403>", + }, + FutureIncompatibleInfo { id: LintId::of(MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT), reference: "RFC 218 <https://github.com/rust-lang/rfcs/blob/\ master/text/0218-empty-struct-with-braces.md>", diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 6cfde27ac97..ce8ede7f4b9 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -12,7 +12,7 @@ #![allow(unreachable_code)] use rustc::dep_graph::DepNode; -use rustc::infer::{self, InferCtxt}; +use rustc::infer::{self, InferCtxt, InferOk}; use rustc::traits::{self, ProjectionMode}; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, Ty, TyCtxt}; @@ -338,6 +338,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { { infer::mk_subty(self.infcx, false, infer::TypeOrigin::Misc(span), sup, sub) + // FIXME(#32730) propagate obligations + .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) } fn mk_eqty(&self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>) @@ -345,6 +347,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { { infer::mk_eqty(self.infcx, false, infer::TypeOrigin::Misc(span), a, b) + // FIXME(#32730) propagate obligations + .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) } fn tcx(&self) -> &'a TyCtxt<'tcx> { diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index a7f00888735..ba5d8ef45b6 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -28,7 +28,7 @@ use rustc::dep_graph::DepNode; use rustc::ty::cast::{CastKind}; use rustc_const_eval::{ConstEvalErr, lookup_const_fn_by_id, compare_lit_exprs}; use rustc_const_eval::{eval_const_expr_partial, lookup_const_by_id}; -use rustc_const_eval::ErrKind::IndexOpFeatureGated; +use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal}; use rustc_const_eval::EvalHint::ExprTypeChecked; use rustc::middle::def::Def; use rustc::middle::def_id::DefId; @@ -110,6 +110,16 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { entry.insert(ConstQualif::empty()); } } + if let Err(err) = eval_const_expr_partial(self.tcx, expr, ExprTypeChecked, None) { + match err.kind { + UnimplementedConstVal(_) => {}, + IndexOpFeatureGated => {}, + _ => self.tcx.sess.add_lint(CONST_ERR, expr.id, expr.span, + format!("constant evaluation error: {}. This will \ + become a HARD ERROR in the future", + err.description())), + } + } self.with_mode(mode, |this| { this.with_euv(None, |euv| euv.consume_expr(expr)); this.visit_expr(expr); @@ -435,6 +445,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { match eval_const_expr_partial( self.tcx, ex, ExprTypeChecked, None) { Ok(_) => {} + Err(ConstEvalErr { kind: UnimplementedConstVal(_), ..}) | Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {}, Err(msg) => { self.tcx.sess.add_lint(CONST_ERR, ex.id, diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index fe44743ede3..f36a8269dc0 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -14,8 +14,7 @@ //! any imports resolved. use DefModifiers; -use resolve_imports::ImportDirective; -use resolve_imports::ImportDirectiveSubclass::{self, SingleImport, GlobImport}; +use resolve_imports::ImportDirectiveSubclass::{self, GlobImport}; use Module; use Namespace::{self, TypeNS, ValueNS}; use {NameBinding, NameBindingKind}; @@ -24,13 +23,14 @@ use Resolver; use {resolve_error, resolve_struct_error, ResolutionError}; use rustc::middle::cstore::{CrateStore, ChildItem, DlDef}; +use rustc::lint; use rustc::middle::def::*; use rustc::middle::def_id::{CRATE_DEF_INDEX, DefId}; use rustc::ty::VariantKind; -use syntax::ast::{Name, NodeId}; +use syntax::ast::Name; use syntax::attr::AttrMetaMethods; -use syntax::parse::token::special_idents; +use syntax::parse::token::{special_idents, SELF_KEYWORD_NAME, SUPER_KEYWORD_NAME}; use syntax::codemap::{Span, DUMMY_SP}; use rustc_front::hir; @@ -117,8 +117,10 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { // Extract and intern the module part of the path. For // globs and lists, the path is found directly in the AST; // for simple paths we have to munge the path a little. - let module_path = match view_path.node { + let is_global; + let module_path: Vec<Name> = match view_path.node { ViewPathSimple(_, ref full_path) => { + is_global = full_path.global; full_path.segments .split_last() .unwrap() @@ -130,6 +132,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { ViewPathGlob(ref module_ident_path) | ViewPathList(ref module_ident_path, _) => { + is_global = module_ident_path.global; module_ident_path.segments .iter() .map(|seg| seg.identifier.name) @@ -137,6 +140,18 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { } }; + // Checking for special identifiers in path + // prevent `self` or `super` at beginning of global path + if is_global && (module_path.first() == Some(&SELF_KEYWORD_NAME) || + module_path.first() == Some(&SUPER_KEYWORD_NAME)) { + self.session.add_lint( + lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH, + item.id, + item.span, + format!("expected identifier, found keyword `{}`", + module_path.first().unwrap().as_str())); + } + // Build up the import directives. let is_prelude = item.attrs.iter().any(|attr| { attr.name() == special_idents::prelude_import.name.as_str() @@ -152,8 +167,8 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { } let subclass = ImportDirectiveSubclass::single(binding, source_name); - self.build_import_directive(parent, - module_path, + self.unresolved_imports += 1; + parent.add_import_directive(module_path, subclass, view_path.span, item.id, @@ -203,8 +218,8 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { } }; let subclass = ImportDirectiveSubclass::single(rename, name); - self.build_import_directive(parent, - module_path, + self.unresolved_imports += 1; + parent.add_import_directive(module_path, subclass, source_item.span, source_item.node.id(), @@ -213,8 +228,8 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { } } ViewPathGlob(_) => { - self.build_import_directive(parent, - module_path, + self.unresolved_imports += 1; + parent.add_import_directive(module_path, GlobImport, view_path.span, item.id, @@ -521,39 +536,6 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { } } - /// Creates and adds an import directive to the given module. - fn build_import_directive(&mut self, - module_: Module<'b>, - module_path: Vec<Name>, - subclass: ImportDirectiveSubclass, - span: Span, - id: NodeId, - is_public: bool, - is_prelude: bool) { - // Bump the reference count on the name. Or, if this is a glob, set - // the appropriate flag. - - match subclass { - SingleImport { target, .. } => { - module_.increment_outstanding_references_for(target, ValueNS, is_public); - module_.increment_outstanding_references_for(target, TypeNS, is_public); - } - GlobImport if !is_prelude => { - // Set the glob flag. This tells us that we don't know the - // module's exports ahead of time. - module_.inc_glob_count(is_public) - } - // Prelude imports are not included in the glob counts since they do not get added to - // `resolved_globs` -- they are handled separately in `resolve_imports`. - GlobImport => {} - } - - let directive = - ImportDirective::new(module_path, subclass, span, id, is_public, is_prelude); - module_.add_import_directive(directive); - self.unresolved_imports += 1; - } - /// Ensures that the reduced graph rooted at the given external module /// is built, building it if it is not. pub fn populate_module_if_necessary(&mut self, module: Module<'b>) { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e5576b00a76..98e40d73133 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -828,8 +828,8 @@ pub struct ModuleS<'a> { // is the NodeId of the local `extern crate` item (otherwise, `extern_crate_id` is None). extern_crate_id: Option<NodeId>, - resolutions: RefCell<HashMap<(Name, Namespace), NameResolution<'a>>>, - unresolved_imports: RefCell<Vec<&'a ImportDirective>>, + resolutions: RefCell<HashMap<(Name, Namespace), &'a RefCell<NameResolution<'a>>>>, + unresolved_imports: RefCell<Vec<&'a ImportDirective<'a>>>, // The module children of this node, including normal modules and anonymous modules. // Anonymous children are pseudo-modules that are implicitly created around items @@ -849,14 +849,8 @@ pub struct ModuleS<'a> { prelude: RefCell<Option<Module<'a>>>, - glob_importers: RefCell<Vec<(Module<'a>, &'a ImportDirective)>>, - resolved_globs: RefCell<(Vec<Module<'a>> /* public */, Vec<Module<'a>> /* private */)>, - - // The number of public glob imports in this module. - public_glob_count: Cell<usize>, - - // The number of private glob imports in this module. - private_glob_count: Cell<usize>, + glob_importers: RefCell<Vec<(Module<'a>, &'a ImportDirective<'a>)>>, + globs: RefCell<Vec<&'a ImportDirective<'a>>>, // Whether this module is populated. If not populated, any attempt to // access the children must be preceded with a @@ -884,22 +878,15 @@ impl<'a> ModuleS<'a> { module_children: RefCell::new(NodeMap()), prelude: RefCell::new(None), glob_importers: RefCell::new(Vec::new()), - resolved_globs: RefCell::new((Vec::new(), Vec::new())), - public_glob_count: Cell::new(0), - private_glob_count: Cell::new(0), + globs: RefCell::new((Vec::new())), populated: Cell::new(!external), arenas: arenas } } - fn add_import_directive(&self, import_directive: ImportDirective) { - let import_directive = self.arenas.alloc_import_directive(import_directive); - self.unresolved_imports.borrow_mut().push(import_directive); - } - fn for_each_child<F: FnMut(Name, Namespace, &'a NameBinding<'a>)>(&self, mut f: F) { for (&(name, ns), name_resolution) in self.resolutions.borrow().iter() { - name_resolution.binding.map(|binding| f(name, ns, binding)); + name_resolution.borrow().binding.map(|binding| f(name, ns, binding)); } } @@ -929,11 +916,6 @@ impl<'a> ModuleS<'a> { _ => false, } } - - fn inc_glob_count(&self, is_public: bool) { - let glob_count = if is_public { &self.public_glob_count } else { &self.private_glob_count }; - glob_count.set(glob_count.get() + 1); - } } impl<'a> fmt::Debug for ModuleS<'a> { @@ -1135,7 +1117,8 @@ pub struct Resolver<'a, 'tcx: 'a> { struct ResolverArenas<'a> { modules: arena::TypedArena<ModuleS<'a>>, name_bindings: arena::TypedArena<NameBinding<'a>>, - import_directives: arena::TypedArena<ImportDirective>, + import_directives: arena::TypedArena<ImportDirective<'a>>, + name_resolutions: arena::TypedArena<RefCell<NameResolution<'a>>>, } impl<'a> ResolverArenas<'a> { @@ -1145,9 +1128,13 @@ impl<'a> ResolverArenas<'a> { fn alloc_name_binding(&'a self, name_binding: NameBinding<'a>) -> &'a NameBinding<'a> { self.name_bindings.alloc(name_binding) } - fn alloc_import_directive(&'a self, import_directive: ImportDirective) -> &'a ImportDirective { + fn alloc_import_directive(&'a self, import_directive: ImportDirective<'a>) + -> &'a ImportDirective { self.import_directives.alloc(import_directive) } + fn alloc_name_resolution(&'a self) -> &'a RefCell<NameResolution<'a>> { + self.name_resolutions.alloc(Default::default()) + } } #[derive(PartialEq)] @@ -1216,6 +1203,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { modules: arena::TypedArena::new(), name_bindings: arena::TypedArena::new(), import_directives: arena::TypedArena::new(), + name_resolutions: arena::TypedArena::new(), } } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 7c5d131dbc5..2aa8925fb54 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -30,7 +30,7 @@ use syntax::codemap::Span; use syntax::util::lev_distance::find_best_match_for_name; use std::mem::replace; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; /// Contains data for specific types of import directives. #[derive(Clone, Debug)] @@ -57,8 +57,9 @@ impl ImportDirectiveSubclass { /// One import directive. #[derive(Debug,Clone)] -pub struct ImportDirective { +pub struct ImportDirective<'a> { module_path: Vec<Name>, + target_module: Cell<Option<Module<'a>>>, // the resolution of `module_path` subclass: ImportDirectiveSubclass, span: Span, id: NodeId, @@ -66,29 +67,11 @@ pub struct ImportDirective { is_prelude: bool, } -impl ImportDirective { - pub fn new(module_path: Vec<Name>, - subclass: ImportDirectiveSubclass, - span: Span, - id: NodeId, - is_public: bool, - is_prelude: bool) - -> ImportDirective { - ImportDirective { - module_path: module_path, - subclass: subclass, - span: span, - id: id, - is_public: is_public, - is_prelude: is_prelude, - } - } - +impl<'a> ImportDirective<'a> { // Given the binding to which this directive resolves in a particular namespace, // this returns the binding for the name this directive defines in that namespace. - fn import<'a>(&self, - binding: &'a NameBinding<'a>, - privacy_error: Option<Box<PrivacyError<'a>>>) -> NameBinding<'a> { + fn import(&self, binding: &'a NameBinding<'a>, privacy_error: Option<Box<PrivacyError<'a>>>) + -> NameBinding<'a> { let mut modifiers = match self.is_public { true => DefModifiers::PUBLIC | DefModifiers::IMPORTABLE, false => DefModifiers::empty(), @@ -110,17 +93,52 @@ impl ImportDirective { } #[derive(Clone, Default)] -/// Records information about the resolution of a name in a module. +/// Records information about the resolution of a name in a namespace of a module. pub struct NameResolution<'a> { - /// The number of unresolved single imports of any visibility that could define the name. - outstanding_references: u32, - /// The number of unresolved `pub` single imports that could define the name. - pub_outstanding_references: u32, + /// The single imports that define the name in the namespace. + single_imports: SingleImports<'a>, /// The least shadowable known binding for this name, or None if there are no known bindings. pub binding: Option<&'a NameBinding<'a>>, duplicate_globs: Vec<&'a NameBinding<'a>>, } +#[derive(Clone, Debug)] +enum SingleImports<'a> { + /// No single imports can define the name in the namespace. + None, + /// Only the given single import can define the name in the namespace. + MaybeOne(&'a ImportDirective<'a>), + /// At least one single import will define the name in the namespace. + AtLeastOne, +} + +impl<'a> Default for SingleImports<'a> { + fn default() -> Self { + SingleImports::None + } +} + +impl<'a> SingleImports<'a> { + fn add_directive(&mut self, directive: &'a ImportDirective<'a>) { + match *self { + SingleImports::None => *self = SingleImports::MaybeOne(directive), + // If two single imports can define the name in the namespace, we can assume that at + // least one of them will define it since otherwise both would have to define only one + // namespace, leading to a duplicate error. + SingleImports::MaybeOne(_) => *self = SingleImports::AtLeastOne, + SingleImports::AtLeastOne => {} + }; + } + + fn directive_failed(&mut self) { + match *self { + SingleImports::None => unreachable!(), + SingleImports::MaybeOne(_) => *self = SingleImports::None, + SingleImports::AtLeastOne => {} + } + } +} + impl<'a> NameResolution<'a> { fn try_define(&mut self, binding: &'a NameBinding<'a>) -> Result<(), &'a NameBinding<'a>> { if let Some(old_binding) = self.binding { @@ -139,40 +157,54 @@ impl<'a> NameResolution<'a> { Ok(()) } + // Returns the binding for the name if it is known or None if it not known. + fn binding(&self) -> Option<&'a NameBinding<'a>> { + self.binding.and_then(|binding| match self.single_imports { + SingleImports::None => Some(binding), + _ if !binding.defined_with(DefModifiers::GLOB_IMPORTED) => Some(binding), + _ => None, // The binding could be shadowed by a single import, so it is not known. + }) + } + // Returns Some(the resolution of the name), or None if the resolution depends // on whether more globs can define the name. - fn try_result(&self, allow_private_imports: bool) + fn try_result(&self, ns: Namespace, allow_private_imports: bool) -> Option<ResolveResult<&'a NameBinding<'a>>> { match self.binding { Some(binding) if !binding.defined_with(DefModifiers::GLOB_IMPORTED) => - Some(Success(binding)), - // If (1) we don't allow private imports, (2) no public single import can define the - // name, and (3) no public glob has defined the name, the resolution depends on globs. - _ if !allow_private_imports && self.pub_outstanding_references == 0 && - !self.binding.map(NameBinding::is_public).unwrap_or(false) => None, - _ if self.outstanding_references > 0 => Some(Indeterminate), - Some(binding) => Some(Success(binding)), - None => None, - } - } - - fn increment_outstanding_references(&mut self, is_public: bool) { - self.outstanding_references += 1; - if is_public { - self.pub_outstanding_references += 1; - } - } - - fn decrement_outstanding_references(&mut self, is_public: bool) { - let decrement_references = |count: &mut _| { - assert!(*count > 0); - *count -= 1; + return Some(Success(binding)), + _ => {} // Items and single imports are not shadowable }; - decrement_references(&mut self.outstanding_references); - if is_public { - decrement_references(&mut self.pub_outstanding_references); + // Check if a single import can still define the name. + match self.single_imports { + SingleImports::None => {}, + SingleImports::AtLeastOne => return Some(Indeterminate), + SingleImports::MaybeOne(directive) => { + // If (1) we don't allow private imports, (2) no public single import can define + // the name, and (3) no public glob has defined the name, the resolution depends + // on whether more globs can define the name. + if !allow_private_imports && !directive.is_public && + !self.binding.map(NameBinding::is_public).unwrap_or(false) { + return None; + } + + let target_module = match directive.target_module.get() { + Some(target_module) => target_module, + None => return Some(Indeterminate), + }; + let name = match directive.subclass { + SingleImport { source, .. } => source, + GlobImport => unreachable!(), + }; + match target_module.resolve_name(name, ns, false) { + Failed(_) => {} + _ => return Some(Indeterminate), + } + } } + + self.binding.map(Success) } fn report_conflicts<F: FnMut(&NameBinding, &NameBinding)>(&self, mut report: F) { @@ -195,15 +227,20 @@ impl<'a> NameResolution<'a> { } impl<'a> ::ModuleS<'a> { + fn resolution(&self, name: Name, ns: Namespace) -> &'a RefCell<NameResolution<'a>> { + *self.resolutions.borrow_mut().entry((name, ns)) + .or_insert_with(|| self.arenas.alloc_name_resolution()) + } + pub fn resolve_name(&self, name: Name, ns: Namespace, allow_private_imports: bool) -> ResolveResult<&'a NameBinding<'a>> { - let resolutions = match self.resolutions.borrow_state() { - ::std::cell::BorrowState::Unused => self.resolutions.borrow(), - _ => return Failed(None), // This happens when there is a cycle of glob imports + let resolution = self.resolution(name, ns); + let resolution = match resolution.borrow_state() { + ::std::cell::BorrowState::Unused => resolution.borrow_mut(), + _ => return Failed(None), // This happens when there is a cycle of imports }; - let resolution = resolutions.get(&(name, ns)).cloned().unwrap_or_default(); - if let Some(result) = resolution.try_result(allow_private_imports) { + if let Some(result) = resolution.try_result(ns, allow_private_imports) { // If the resolution doesn't depend on glob definability, check privacy and return. return result.and_then(|binding| { let allowed = allow_private_imports || !binding.is_import() || binding.is_public(); @@ -211,29 +248,15 @@ impl<'a> ::ModuleS<'a> { }); } - let (ref mut public_globs, ref mut private_globs) = *self.resolved_globs.borrow_mut(); - - // Check if the public globs are determined - if public_globs.len() < self.public_glob_count.get() { - return Indeterminate; - } - for module in public_globs.iter() { - if let Indeterminate = module.resolve_name(name, ns, false) { - return Indeterminate; - } - } - - if !allow_private_imports { - return Failed(None); - } - - // Check if the private globs are determined - if private_globs.len() < self.private_glob_count.get() { - return Indeterminate; - } - for module in private_globs.iter() { - if let Indeterminate = module.resolve_name(name, ns, false) { - return Indeterminate; + // Check if the globs are determined + for directive in self.globs.borrow().iter() { + if !allow_private_imports && !directive.is_public { continue } + match directive.target_module.get() { + None => return Indeterminate, + Some(target_module) => match target_module.resolve_name(name, ns, false) { + Indeterminate => return Indeterminate, + _ => {} + } } } @@ -243,7 +266,7 @@ impl<'a> ::ModuleS<'a> { // Invariant: this may not be called until import resolution is complete. pub fn resolve_name_in_lexical_scope(&self, name: Name, ns: Namespace) -> Option<&'a NameBinding<'a>> { - self.resolutions.borrow().get(&(name, ns)).and_then(|resolution| resolution.binding) + self.resolution(name, ns).borrow().binding .or_else(|| self.prelude.borrow().and_then(|prelude| { prelude.resolve_name(name, ns, false).success() })) @@ -258,9 +281,36 @@ impl<'a> ::ModuleS<'a> { }) } - pub fn increment_outstanding_references_for(&self, name: Name, ns: Namespace, is_public: bool) { - self.resolutions.borrow_mut().entry((name, ns)).or_insert_with(Default::default) - .increment_outstanding_references(is_public); + pub fn add_import_directive(&self, + module_path: Vec<Name>, + subclass: ImportDirectiveSubclass, + span: Span, + id: NodeId, + is_public: bool, + is_prelude: bool) { + let directive = self.arenas.alloc_import_directive(ImportDirective { + module_path: module_path, + target_module: Cell::new(None), + subclass: subclass, + span: span, + id: id, + is_public: is_public, + is_prelude: is_prelude, + }); + + self.unresolved_imports.borrow_mut().push(directive); + match directive.subclass { + SingleImport { target, .. } => { + for &ns in &[ValueNS, TypeNS] { + self.resolution(target, ns).borrow_mut().single_imports + .add_directive(directive); + } + } + // We don't add prelude imports to the globs since they only affect lexical scopes, + // which are not relevant to import resolution. + GlobImport if directive.is_prelude => {} + GlobImport => self.globs.borrow_mut().push(directive), + } } // Use `update` to mutate the resolution for the name. @@ -268,13 +318,12 @@ impl<'a> ::ModuleS<'a> { fn update_resolution<T, F>(&self, name: Name, ns: Namespace, update: F) -> T where F: FnOnce(&mut NameResolution<'a>) -> T { - let mut resolutions = self.resolutions.borrow_mut(); - let resolution = resolutions.entry((name, ns)).or_insert_with(Default::default); - let was_success = resolution.try_result(false).and_then(ResolveResult::success).is_some(); + let mut resolution = &mut *self.resolution(name, ns).borrow_mut(); + let was_known = resolution.binding().is_some(); let t = update(resolution); - if !was_success { - if let Some(Success(binding)) = resolution.try_result(false) { + if !was_known { + if let Some(binding) = resolution.binding() { self.define_in_glob_importers(name, ns, binding); } } @@ -292,7 +341,7 @@ impl<'a> ::ModuleS<'a> { struct ImportResolvingError<'a> { /// Module where the error happened source_module: Module<'a>, - import_directive: &'a ImportDirective, + import_directive: &'a ImportDirective<'a>, span: Span, help: String, } @@ -424,19 +473,23 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { /// don't know whether the name exists at the moment due to other /// currently-unresolved imports, or success if we know the name exists. /// If successful, the resolved bindings are written into the module. - fn resolve_import(&mut self, directive: &'b ImportDirective) -> ResolveResult<()> { + fn resolve_import(&mut self, directive: &'b ImportDirective<'b>) -> ResolveResult<()> { debug!("(resolving import for module) resolving import `{}::...` in `{}`", names_to_string(&directive.module_path), module_to_string(self.resolver.current_module)); - let target_module = match self.resolver.resolve_module_path(&directive.module_path, - DontUseLexicalScope, - directive.span) { - Success(module) => module, - Indeterminate => return Indeterminate, - Failed(err) => return Failed(err), + let target_module = match directive.target_module.get() { + Some(module) => module, + _ => match self.resolver.resolve_module_path(&directive.module_path, + DontUseLexicalScope, + directive.span) { + Success(module) => module, + Indeterminate => return Indeterminate, + Failed(err) => return Failed(err), + }, }; + directive.target_module.set(Some(target_module)); let (source, target, value_determined, type_determined) = match directive.subclass { SingleImport { source, target, ref value_determined, ref type_determined } => (source, target, value_determined, type_determined), @@ -444,26 +497,12 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { }; // We need to resolve both namespaces for this to succeed. - let module_ = self.resolver.current_module; - let (value_result, type_result) = { - let mut resolve_in_ns = |ns, determined: bool| { - // Temporarily count the directive as determined so that the resolution fails - // (as opposed to being indeterminate) when it can only be defined by the directive. - if !determined { - module_.resolutions.borrow_mut().get_mut(&(target, ns)).unwrap() - .decrement_outstanding_references(directive.is_public); - } - let result = - self.resolver.resolve_name_in_module(target_module, source, ns, false, true); - if !determined { - module_.increment_outstanding_references_for(target, ns, directive.is_public) - } - result - }; - (resolve_in_ns(ValueNS, value_determined.get()), - resolve_in_ns(TypeNS, type_determined.get())) - }; + let value_result = + self.resolver.resolve_name_in_module(target_module, source, ValueNS, false, true); + let type_result = + self.resolver.resolve_name_in_module(target_module, source, TypeNS, false, true); + let module_ = self.resolver.current_module; for &(ns, result, determined) in &[(ValueNS, &value_result, value_determined), (TypeNS, &type_result, type_determined)] { if determined.get() { continue } @@ -488,18 +527,24 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { let binding = &directive.import(binding, None); self.resolver.report_conflict(module_, target, ns, binding, old_binding); } + } else { + module_.update_resolution(target, ns, |resolution| { + resolution.single_imports.directive_failed(); + }); } - - module_.update_resolution(target, ns, |resolution| { - resolution.decrement_outstanding_references(directive.is_public); - }) } match (&value_result, &type_result) { (&Indeterminate, _) | (_, &Indeterminate) => return Indeterminate, (&Failed(_), &Failed(_)) => { - let children = target_module.resolutions.borrow(); - let names = children.keys().map(|&(ref name, _)| name); + let resolutions = target_module.resolutions.borrow(); + let names = resolutions.iter().filter_map(|(&(ref name, _), resolution)| { + match *resolution.borrow() { + NameResolution { binding: Some(_), .. } => Some(name), + NameResolution { single_imports: SingleImports::None, .. } => None, + _ => Some(name), + } + }); let lev_suggestion = match find_best_match_for_name(names, &source.as_str(), None) { Some(name) => format!(". Did you mean to use `{}`?", name), None => "".to_owned(), @@ -579,7 +624,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { // succeeds or bails out (as importing * from an empty module or a module // that exports nothing is valid). target_module is the module we are // actually importing, i.e., `foo` in `use foo::*`. - fn resolve_glob_import(&mut self, target_module: Module<'b>, directive: &'b ImportDirective) + fn resolve_glob_import(&mut self, target_module: Module<'b>, directive: &'b ImportDirective<'b>) -> ResolveResult<()> { if let Some(Def::Trait(_)) = target_module.def { self.resolver.session.span_err(directive.span, "items in traits are not importable."); @@ -598,15 +643,11 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { return Success(()); } - // Add to target_module's glob_importers and module_'s resolved_globs + // Add to target_module's glob_importers target_module.glob_importers.borrow_mut().push((module_, directive)); - match *module_.resolved_globs.borrow_mut() { - (ref mut public_globs, _) if directive.is_public => public_globs.push(target_module), - (_, ref mut private_globs) => private_globs.push(target_module), - } for (&(name, ns), resolution) in target_module.resolutions.borrow().iter() { - if let Some(Success(binding)) = resolution.try_result(false) { + if let Some(binding) = resolution.borrow().binding() { if binding.defined_with(DefModifiers::IMPORTABLE | DefModifiers::PUBLIC) { let _ = module_.try_define_child(name, ns, directive.import(binding, None)); } @@ -630,11 +671,11 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { // reporting conflicts, reporting the PRIVATE_IN_PUBLIC lint, and reporting unresolved imports. fn finalize_resolutions(&mut self, module: Module<'b>, report_unresolved_imports: bool) { // Since import resolution is finished, globs will not define any more names. - module.public_glob_count.set(0); module.private_glob_count.set(0); - *module.resolved_globs.borrow_mut() = (Vec::new(), Vec::new()); + *module.globs.borrow_mut() = Vec::new(); let mut reexports = Vec::new(); for (&(name, ns), resolution) in module.resolutions.borrow().iter() { + let resolution = resolution.borrow(); resolution.report_conflicts(|b1, b2| { self.resolver.report_conflict(module, name, ns, b1, b2) }); diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index a323d63adae..6b0945b2bb2 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -541,14 +541,6 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } }; - let llfn = declare::declare_fn(ccx, &sym, ty); - attributes::from_fn_attrs(ccx, attrs, llfn); - if let Some(id) = local_item { - // FIXME(eddyb) Doubt all extern fn should allow unwinding. - attributes::unwind(llfn, true); - ccx.item_symbols().borrow_mut().insert(id, sym); - } - // This is subtle and surprising, but sometimes we have to bitcast // the resulting fn pointer. The reason has to do with external // functions. If you have two crates that both bind the same C @@ -572,12 +564,32 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // This can occur on either a crate-local or crate-external // reference. It also occurs when testing libcore and in some // other weird situations. Annoying. + let llptrty = type_of::type_of(ccx, fn_ptr_ty); - let llfn = if common::val_ty(llfn) != llptrty { - debug!("get_fn: casting {:?} to {:?}", llfn, llptrty); - consts::ptrcast(llfn, llptrty) + let llfn = if let Some(llfn) = declare::get_declared_value(ccx, &sym) { + if common::val_ty(llfn) != llptrty { + if local_item.is_some() { + bug!("symbol `{}` previously declared as {:?}, now wanted as {:?}", + sym, Value(llfn), llptrty); + } + debug!("get_fn: casting {:?} to {:?}", llfn, llptrty); + consts::ptrcast(llfn, llptrty) + } else { + debug!("get_fn: not casting pointer!"); + llfn + } } else { + let llfn = declare::declare_fn(ccx, &sym, ty); + assert_eq!(common::val_ty(llfn), llptrty); debug!("get_fn: not casting pointer!"); + + attributes::from_fn_attrs(ccx, attrs, llfn); + if let Some(id) = local_item { + // FIXME(eddyb) Doubt all extern fn should allow unwinding. + attributes::unwind(llfn, true); + ccx.item_symbols().borrow_mut().insert(id, sym); + } + llfn }; diff --git a/src/librustc_trans/declare.rs b/src/librustc_trans/declare.rs index 83af511f087..eb520fe744a 100644 --- a/src/librustc_trans/declare.rs +++ b/src/librustc_trans/declare.rs @@ -26,6 +26,7 @@ use abi::{Abi, FnType}; use attributes; use context::CrateContext; use type_::Type; +use value::Value; use std::ffi::CString; @@ -146,27 +147,33 @@ pub fn define_internal_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } -/// Get defined or externally defined (AvailableExternally linkage) value by -/// name. -pub fn get_defined_value(ccx: &CrateContext, name: &str) -> Option<ValueRef> { - debug!("get_defined_value(name={:?})", name); +/// Get declared value by name. +pub fn get_declared_value(ccx: &CrateContext, name: &str) -> Option<ValueRef> { + debug!("get_declared_value(name={:?})", name); let namebuf = CString::new(name).unwrap_or_else(|_|{ bug!("name {:?} contains an interior null byte", name) }); let val = unsafe { llvm::LLVMGetNamedValue(ccx.llmod(), namebuf.as_ptr()) }; if val.is_null() { - debug!("get_defined_value: {:?} value is null", name); + debug!("get_declared_value: {:?} value is null", name); None } else { + debug!("get_declared_value: {:?} => {:?}", name, Value(val)); + Some(val) + } +} + +/// Get defined or externally defined (AvailableExternally linkage) value by +/// name. +pub fn get_defined_value(ccx: &CrateContext, name: &str) -> Option<ValueRef> { + get_declared_value(ccx, name).and_then(|val|{ let declaration = unsafe { llvm::LLVMIsDeclaration(val) != 0 }; - debug!("get_defined_value: found {:?} value (declaration: {})", - name, declaration); if !declaration { Some(val) } else { None } - } + }) } diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 219f970835a..28d5d9ac176 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -752,33 +752,47 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, match split[1] { "cxchg" | "cxchgweak" => { - let cmp = from_immediate(bcx, llargs[1]); - let src = from_immediate(bcx, llargs[2]); - let ptr = PointerCast(bcx, llargs[0], val_ty(src).ptr_to()); - let weak = if split[1] == "cxchgweak" { llvm::True } else { llvm::False }; - let val = AtomicCmpXchg(bcx, ptr, cmp, src, order, failorder, weak); - let result = ExtractValue(bcx, val, 0); - let success = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx())); - Store(bcx, - result, - PointerCast(bcx, StructGEP(bcx, llresult, 0), val_ty(src).ptr_to())); - Store(bcx, success, StructGEP(bcx, llresult, 1)); + let sty = &substs.types.get(FnSpace, 0).sty; + if int_type_width_signed(sty, ccx).is_some() { + let weak = if split[1] == "cxchgweak" { llvm::True } else { llvm::False }; + let val = AtomicCmpXchg(bcx, llargs[0], llargs[1], llargs[2], + order, failorder, weak); + let result = ExtractValue(bcx, val, 0); + let success = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool(bcx.ccx())); + Store(bcx, result, StructGEP(bcx, llresult, 0)); + Store(bcx, success, StructGEP(bcx, llresult, 1)); + } else { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, sty)); + } C_nil(ccx) } "load" => { - let tp_ty = *substs.types.get(FnSpace, 0); - let mut ptr = llargs[0]; - if let Some(ty) = fn_ty.ret.cast { - ptr = PointerCast(bcx, ptr, ty.ptr_to()); + let sty = &substs.types.get(FnSpace, 0).sty; + if int_type_width_signed(sty, ccx).is_some() { + AtomicLoad(bcx, llargs[0], order) + } else { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, sty)); + C_nil(ccx) } - to_immediate(bcx, AtomicLoad(bcx, ptr, order), tp_ty) } "store" => { - let val = from_immediate(bcx, llargs[1]); - let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to()); - AtomicStore(bcx, val, ptr, order); + let sty = &substs.types.get(FnSpace, 0).sty; + if int_type_width_signed(sty, ccx).is_some() { + AtomicStore(bcx, llargs[1], llargs[0], order); + } else { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, sty)); + } C_nil(ccx) } @@ -809,9 +823,16 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, _ => ccx.sess().fatal("unknown atomic operation") }; - let val = from_immediate(bcx, llargs[1]); - let ptr = PointerCast(bcx, llargs[0], val_ty(val).ptr_to()); - AtomicRMW(bcx, atom_op, ptr, val, order) + let sty = &substs.types.get(FnSpace, 0).sty; + if int_type_width_signed(sty, ccx).is_some() { + AtomicRMW(bcx, atom_op, llargs[0], llargs[1], order) + } else { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, sty)); + C_nil(ccx) + } } } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 6dad3ff850b..55c3a5add37 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -9,7 +9,7 @@ // except according to those terms. use middle::def::{self, Def}; -use rustc::infer::{self, TypeOrigin}; +use rustc::infer::{self, InferOk, TypeOrigin}; use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding}; use middle::pat_util::pat_is_resolved_const; use rustc::ty::subst::Substs; @@ -531,7 +531,12 @@ pub fn check_match<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, }; let result = if is_if_let_fallback { - fcx.infcx().eq_types(true, origin, arm_ty, result_ty).map(|_| arm_ty) + fcx.infcx().eq_types(true, origin, arm_ty, result_ty) + .map(|InferOk { obligations, .. }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + arm_ty + }) } else if i == 0 { // Special-case the first arm, as it has no "previous expressions". coercion::try(fcx, &arm.body, coerce_first) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 70467e2d2dd..cafd0519c28 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -62,7 +62,7 @@ use check::{autoderef, FnCtxt, UnresolvedTypeAction}; -use rustc::infer::{Coercion, TypeOrigin, TypeTrace}; +use rustc::infer::{Coercion, InferOk, TypeOrigin, TypeTrace}; use rustc::traits::{self, ObligationCause}; use rustc::traits::{predicate_for_trait_def, report_selection_error}; use rustc::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef}; @@ -118,8 +118,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let trace = TypeTrace::types(self.origin, false, a, b); if self.use_lub { infcx.lub(false, trace, &a, &b) + .map(|InferOk { value, obligations }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + value + }) } else { infcx.sub(false, trace, &a, &b) + .map(|InferOk { value, obligations }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + value + }) } }) } @@ -655,12 +665,22 @@ pub fn try_find_lub<'a, 'b, 'tcx, E, I>(fcx: &FnCtxt<'a, 'tcx>, (&ty::TyFnDef(a_def_id, a_substs, a_fty), &ty::TyFnDef(b_def_id, b_substs, b_fty)) => { // The signature must always match. - let fty = fcx.infcx().lub(true, trace.clone(), a_fty, b_fty)?; + let fty = fcx.infcx().lub(true, trace.clone(), a_fty, b_fty) + .map(|InferOk { value, obligations }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + value + })?; if a_def_id == b_def_id { // Same function, maybe the parameters match. let substs = fcx.infcx().commit_if_ok(|_| { fcx.infcx().lub(true, trace.clone(), a_substs, b_substs) + .map(|InferOk { value, obligations }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + value + }) }).map(|s| fcx.tcx().mk_substs(s)); if let Ok(substs) = substs { @@ -724,6 +744,11 @@ pub fn try_find_lub<'a, 'b, 'tcx, E, I>(fcx: &FnCtxt<'a, 'tcx>, if !noop { return fcx.infcx().commit_if_ok(|_| { fcx.infcx().lub(true, trace.clone(), &prev_ty, &new_ty) + .map(|InferOk { value, obligations }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + value + }) }); } } @@ -736,6 +761,11 @@ pub fn try_find_lub<'a, 'b, 'tcx, E, I>(fcx: &FnCtxt<'a, 'tcx>, } else { fcx.infcx().commit_if_ok(|_| { fcx.infcx().lub(true, trace, &prev_ty, &new_ty) + .map(|InferOk { value, obligations }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + value + }) }) } } diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 6d429fa7b73..3c12ab8d598 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -9,7 +9,7 @@ // except according to those terms. use middle::free_region::FreeRegionMap; -use rustc::infer::{self, TypeOrigin}; +use rustc::infer::{self, InferOk, TypeOrigin}; use rustc::ty::{self, TyCtxt}; use rustc::traits::{self, ProjectionMode}; use rustc::ty::subst::{self, Subst, Substs, VecPerParamSpace}; @@ -475,7 +475,10 @@ pub fn compare_const_impl<'tcx>(tcx: &TyCtxt<'tcx>, }); match err { - Ok(()) => { } + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()) + } Err(terr) => { debug!("checking associated const for compatibility: impl ty {:?}, trait ty {:?}", impl_ty, diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index d8bdf6c61aa..bc2ef9aafee 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -11,7 +11,7 @@ use check::{coercion, FnCtxt}; use rustc::ty::Ty; -use rustc::infer::TypeOrigin; +use rustc::infer::{InferOk, TypeOrigin}; use syntax::codemap::Span; use rustc_front::hir; @@ -21,16 +21,28 @@ use rustc_front::hir; pub fn suptype<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { let origin = TypeOrigin::Misc(sp); - if let Err(e) = fcx.infcx().sub_types(false, origin, actual, expected) { - fcx.infcx().report_mismatched_types(origin, expected, actual, e); + match fcx.infcx().sub_types(false, origin, actual, expected) { + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + }, + Err(e) => { + fcx.infcx().report_mismatched_types(origin, expected, actual, e); + } } } pub fn eqtype<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { let origin = TypeOrigin::Misc(sp); - if let Err(e) = fcx.infcx().eq_types(false, origin, actual, expected) { - fcx.infcx().report_mismatched_types(origin, expected, actual, e); + match fcx.infcx().eq_types(false, origin, actual, expected) { + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + }, + Err(e) => { + fcx.infcx().report_mismatched_types(origin, expected, actual, e); + } } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index c4a8022071b..2e15ac0e130 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -21,8 +21,7 @@ use rustc::ty::subst; use rustc::ty::subst::Subst; use rustc::traits; use rustc::ty::{self, NoPreference, Ty, TyCtxt, ToPolyTraitRef, TraitRef, TypeFoldable}; -use rustc::infer; -use rustc::infer::{InferCtxt, TypeOrigin}; +use rustc::infer::{self, InferCtxt, InferOk, TypeOrigin}; use syntax::ast; use syntax::codemap::{Span, DUMMY_SP}; use rustc_front::hir; @@ -1151,6 +1150,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn make_sub_ty(&self, sub: Ty<'tcx>, sup: Ty<'tcx>) -> infer::UnitResult<'tcx> { self.infcx().sub_types(false, TypeOrigin::Misc(DUMMY_SP), sub, sup) + // FIXME(#32730) propagate obligations + .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) } fn has_applicable_self(&self, item: &ty::ImplOrTraitItem) -> bool { diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 4d806bda484..32b5a63817e 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -14,7 +14,7 @@ use CrateCtxt; use astconv::AstConv; -use check::{self, FnCtxt}; +use check::{self, FnCtxt, UnresolvedTypeAction, autoderef}; use front::map as hir_map; use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable}; use middle::cstore::{self, CrateStore}; @@ -22,6 +22,7 @@ use middle::def::Def; use middle::def_id::DefId; use middle::lang_items::FnOnceTraitLangItem; use rustc::ty::subst::Substs; +use rustc::ty::LvaluePreference; use rustc::traits::{Obligation, SelectionContext}; use util::nodemap::{FnvHashSet}; @@ -50,23 +51,37 @@ fn is_fn_ty<'a, 'tcx>(ty: &Ty<'tcx>, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> bool if let Ok(fn_once_trait_did) = cx.lang_items.require(FnOnceTraitLangItem) { let infcx = fcx.infcx(); - infcx.probe(|_| { - let fn_once_substs = - Substs::new_trait(vec![infcx.next_ty_var()], - Vec::new(), - ty); - let trait_ref = - ty::TraitRef::new(fn_once_trait_did, - cx.mk_substs(fn_once_substs)); - let poly_trait_ref = trait_ref.to_poly_trait_ref(); - let obligation = Obligation::misc(span, - fcx.body_id, - poly_trait_ref - .to_predicate()); - let mut selcx = SelectionContext::new(infcx); - - return selcx.evaluate_obligation(&obligation) - }) + let (_, _, opt_is_fn) = autoderef(fcx, + span, + ty, + || None, + UnresolvedTypeAction::Ignore, + LvaluePreference::NoPreference, + |ty, _| { + infcx.probe(|_| { + let fn_once_substs = + Substs::new_trait(vec![infcx.next_ty_var()], + Vec::new(), + ty); + let trait_ref = + ty::TraitRef::new(fn_once_trait_did, + cx.mk_substs(fn_once_substs)); + let poly_trait_ref = trait_ref.to_poly_trait_ref(); + let obligation = Obligation::misc(span, + fcx.body_id, + poly_trait_ref + .to_predicate()); + let mut selcx = SelectionContext::new(infcx); + + if selcx.evaluate_obligation(&obligation) { + Some(()) + } else { + None + } + }) + }); + + opt_is_fn.is_some() } else { false } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 6c3469c9d72..e485fbe1621 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -88,8 +88,7 @@ use middle::astconv_util::prohibit_type_params; use middle::cstore::LOCAL_CRATE; use middle::def::{self, Def}; use middle::def_id::DefId; -use rustc::infer; -use rustc::infer::{TypeOrigin, TypeTrace, type_variable}; +use rustc::infer::{self, InferOk, TypeOrigin, TypeTrace, type_variable}; use middle::pat_util::{self, pat_id_map}; use rustc::ty::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace}; use rustc::traits::{self, report_fulfillment_errors, ProjectionMode}; @@ -1627,6 +1626,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sup: Ty<'tcx>) -> Result<(), TypeError<'tcx>> { infer::mk_subty(self.infcx(), a_is_expected, origin, sub, sup) + // FIXME(#32730) propagate obligations + .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) } pub fn mk_eqty(&self, @@ -1636,6 +1637,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sup: Ty<'tcx>) -> Result<(), TypeError<'tcx>> { infer::mk_eqty(self.infcx(), a_is_expected, origin, sub, sup) + // FIXME(#32730) propagate obligations + .map(|InferOk { obligations, .. }| assert!(obligations.is_empty())) } pub fn mk_subr(&self, @@ -1914,7 +1917,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match infer::mk_eqty(self.infcx(), false, TypeOrigin::Misc(default.origin_span), ty, default.ty) { - Ok(()) => {} + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()) + }, Err(_) => { conflicts.push((*ty, default)); } @@ -2007,7 +2013,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match infer::mk_eqty(self.infcx(), false, TypeOrigin::Misc(default.origin_span), ty, default.ty) { - Ok(()) => {} + // FIXME(#32730) propagate obligations + Ok(InferOk { obligations, .. }) => assert!(obligations.is_empty()), Err(_) => { result = Some(default); } @@ -2773,8 +2780,10 @@ fn expected_types_for_fn_args<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, let ures = fcx.infcx().sub_types(false, origin, formal_ret_ty, ret_ty); // FIXME(#15760) can't use try! here, FromError doesn't default // to identity so the resulting type is not constrained. - if let Err(e) = ures { - return Err(e); + match ures { + // FIXME(#32730) propagate obligations + Ok(InferOk { obligations, .. }) => assert!(obligations.is_empty()), + Err(e) => return Err(e), } // Record all the argument types, with the substitutions @@ -2902,13 +2911,23 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, fcx.infcx().commit_if_ok(|_| { let trace = TypeTrace::types(origin, true, then_ty, else_ty); fcx.infcx().lub(true, trace, &then_ty, &else_ty) + .map(|InferOk { value, obligations }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + value + }) }) }; (origin, then_ty, else_ty, result) } else { let origin = TypeOrigin::IfExpressionWithNoElse(sp); (origin, unit, then_ty, - fcx.infcx().eq_types(true, origin, unit, then_ty).map(|_| unit)) + fcx.infcx().eq_types(true, origin, unit, then_ty) + .map(|InferOk { obligations, .. }| { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + unit + })) }; let if_ty = match result { diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 283245bd6f4..15bf6671de5 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -92,7 +92,7 @@ use middle::region::{self, CodeExtent}; use rustc::ty::subst::Substs; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt, MethodCall, TypeFoldable}; -use rustc::infer::{self, GenericKind, InferCtxt, SubregionOrigin, TypeOrigin, VerifyBound}; +use rustc::infer::{self, GenericKind, InferCtxt, InferOk, SubregionOrigin, TypeOrigin, VerifyBound}; use middle::pat_util; use rustc::ty::adjustment; use rustc::ty::wf::ImpliedBound; @@ -1841,7 +1841,11 @@ fn declared_projection_bounds_from_trait<'a,'tcx>(rcx: &Rcx<'a, 'tcx>, // check whether this predicate applies to our current projection match infer::mk_eqty(infcx, false, TypeOrigin::Misc(span), ty, outlives.0) { - Ok(()) => { Ok(outlives.1) } + Ok(InferOk { obligations, .. }) => { + // FIXME(#32730) propagate obligations + assert!(obligations.is_empty()); + Ok(outlives.1) + } Err(_) => { Err(()) } } }); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b26e56008ac..7437d608771 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -14,7 +14,6 @@ pub use self::Type::*; pub use self::PrimitiveType::*; pub use self::TypeKind::*; -pub use self::StructField::*; pub use self::VariantKind::*; pub use self::Mutability::*; pub use self::Import::*; @@ -53,6 +52,7 @@ use std::env::current_dir; use core::DocContext; use doctree; use visit_ast; +use html::item_type::ItemType; /// A stable identifier to the particular version of JSON output. /// Increment this when the `Crate` and related structures change. @@ -273,36 +273,49 @@ impl Item { } pub fn is_crate(&self) -> bool { match self.inner { - ModuleItem(Module { items: _, is_crate: true }) => true, - _ => false + StrippedItem(box ModuleItem(Module { is_crate: true, ..})) | + ModuleItem(Module { is_crate: true, ..}) => true, + _ => false, } } pub fn is_mod(&self) -> bool { - match self.inner { ModuleItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::Module } pub fn is_trait(&self) -> bool { - match self.inner { TraitItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::Trait } pub fn is_struct(&self) -> bool { - match self.inner { StructItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::Struct } pub fn is_enum(&self) -> bool { - match self.inner { EnumItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::Module } pub fn is_fn(&self) -> bool { - match self.inner { FunctionItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::Function } pub fn is_associated_type(&self) -> bool { - match self.inner { AssociatedTypeItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::AssociatedType } pub fn is_associated_const(&self) -> bool { - match self.inner { AssociatedConstItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::AssociatedConst } pub fn is_method(&self) -> bool { - match self.inner { MethodItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::Method } pub fn is_ty_method(&self) -> bool { - match self.inner { TyMethodItem(..) => true, _ => false } + ItemType::from_item(self) == ItemType::TyMethod + } + pub fn is_stripped(&self) -> bool { + match self.inner { StrippedItem(..) => true, _ => false } + } + pub fn has_stripped_fields(&self) -> Option<bool> { + match self.inner { + StructItem(ref _struct) => Some(_struct.fields_stripped), + VariantItem(Variant { kind: StructVariant(ref vstruct)} ) => { + Some(vstruct.fields_stripped) + }, + _ => None, + } } pub fn stability_class(&self) -> String { @@ -341,7 +354,7 @@ pub enum ItemEnum { TyMethodItem(TyMethod), /// A method with a body. MethodItem(Method), - StructFieldItem(StructField), + StructFieldItem(Type), VariantItem(Variant), /// `fn`s from an extern block ForeignFunctionItem(Function), @@ -352,6 +365,8 @@ pub enum ItemEnum { AssociatedConstItem(Type, Option<String>), AssociatedTypeItem(Vec<TyParamBound>, Option<Type>), DefaultImplItem(DefaultImpl), + /// An item that has been stripped by a rustdoc pass + StrippedItem(Box<ItemEnum>), } #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] @@ -1733,12 +1748,6 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] -pub enum StructField { - HiddenStructField, // inserted later by strip passes - TypedStructField(Type), -} - impl Clean<Item> for hir::StructField { fn clean(&self, cx: &DocContext) -> Item { Item { @@ -1749,7 +1758,7 @@ impl Clean<Item> for hir::StructField { stability: get_stability(cx, cx.map.local_def_id(self.id)), deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)), def_id: cx.map.local_def_id(self.id), - inner: StructFieldItem(TypedStructField(self.ty.clean(cx))), + inner: StructFieldItem(self.ty.clean(cx)), } } } @@ -1766,7 +1775,7 @@ impl<'tcx> Clean<Item> for ty::FieldDefData<'tcx, 'static> { stability: get_stability(cx, self.did), deprecation: get_deprecation(cx, self.did), def_id: self.did, - inner: StructFieldItem(TypedStructField(self.unsubst_ty().clean(cx))), + inner: StructFieldItem(self.unsubst_ty().clean(cx)), } } } @@ -1897,9 +1906,7 @@ impl<'tcx> Clean<Item> for ty::VariantDefData<'tcx, 'static> { def_id: field.did, stability: get_stability(cx, field.did), deprecation: get_deprecation(cx, field.did), - inner: StructFieldItem( - TypedStructField(field.unsubst_ty().clean(cx)) - ) + inner: StructFieldItem(field.unsubst_ty().clean(cx)) } }).collect() }) diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index ceec80402c0..5595c749256 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -10,28 +10,50 @@ use clean::*; +pub enum FoldItem { + Retain(Item), + Strip(Item), + Erase, +} + +impl FoldItem { + pub fn fold(self) -> Option<Item> { + match self { + FoldItem::Erase => None, + FoldItem::Retain(i) => Some(i), + FoldItem::Strip(item@ Item { inner: StrippedItem(..), .. } ) => Some(item), + FoldItem::Strip(mut i) => { + i.inner = StrippedItem(box i.inner); + Some(i) + } + } + } +} + pub trait DocFolder : Sized { fn fold_item(&mut self, item: Item) -> Option<Item> { self.fold_item_recur(item) } /// don't override! - fn fold_item_recur(&mut self, item: Item) -> Option<Item> { - let Item { attrs, name, source, visibility, def_id, inner, stability, deprecation } = item; - let inner = match inner { + fn fold_inner_recur(&mut self, inner: ItemEnum) -> ItemEnum { + match inner { + StrippedItem(..) => unreachable!(), + ModuleItem(i) => { + ModuleItem(self.fold_mod(i)) + }, StructItem(mut i) => { let num_fields = i.fields.len(); i.fields = i.fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); - i.fields_stripped |= num_fields != i.fields.len(); + i.fields_stripped |= num_fields != i.fields.len() || + i.fields.iter().any(|f| f.is_stripped()); StructItem(i) }, - ModuleItem(i) => { - ModuleItem(self.fold_mod(i)) - }, EnumItem(mut i) => { let num_variants = i.variants.len(); i.variants = i.variants.into_iter().filter_map(|x| self.fold_item(x)).collect(); - i.variants_stripped |= num_variants != i.variants.len(); + i.variants_stripped |= num_variants != i.variants.len() || + i.variants.iter().any(|f| f.is_stripped()); EnumItem(i) }, TraitItem(mut i) => { @@ -48,13 +70,24 @@ pub trait DocFolder : Sized { StructVariant(mut j) => { let num_fields = j.fields.len(); j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); - j.fields_stripped |= num_fields != j.fields.len(); + j.fields_stripped |= num_fields != j.fields.len() || + j.fields.iter().any(|f| f.is_stripped()); VariantItem(Variant {kind: StructVariant(j), ..i2}) }, _ => VariantItem(i2) } }, x => x + } + } + + /// don't override! + fn fold_item_recur(&mut self, item: Item) -> Option<Item> { + let Item { attrs, name, source, visibility, def_id, inner, stability, deprecation } = item; + + let inner = match inner { + StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)), + _ => self.fold_inner_recur(inner), }; Some(Item { attrs: attrs, name: name, source: source, inner: inner, @@ -70,9 +103,8 @@ pub trait DocFolder : Sized { } fn fold_crate(&mut self, mut c: Crate) -> Crate { - c.module = c.module.and_then(|module| { - self.fold_item(module) - }); + c.module = c.module.and_then(|module| self.fold_item(module)); + c.external_traits = c.external_traits.into_iter().map(|(k, mut v)| { v.items = v.items.into_iter().filter_map(|i| self.fold_item(i)).collect(); (k, v) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index b3cad90ccb5..7ca4703a2e1 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -17,22 +17,36 @@ use html::escape::Escape; use std::io; use std::io::prelude::*; -use syntax::parse::lexer; +use syntax::parse::lexer::{self, Reader}; use syntax::parse::token; use syntax::parse; -/// Highlights some source code, returning the HTML output. -pub fn highlight(src: &str, class: Option<&str>, id: Option<&str>) -> String { +/// Highlights `src`, returning the HTML output. +pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str>) -> String { debug!("highlighting: ================\n{}\n==============", src); let sess = parse::ParseSess::new(); let fm = sess.codemap().new_filemap("<stdin>".to_string(), src.to_string()); let mut out = Vec::new(); - doit(&sess, - lexer::StringReader::new(&sess.span_diagnostic, fm), - class, - id, - &mut out).unwrap(); + write_header(class, id, &mut out).unwrap(); + write_source(&sess, + lexer::StringReader::new(&sess.span_diagnostic, fm), + &mut out).unwrap(); + write_footer(&mut out).unwrap(); + String::from_utf8_lossy(&out[..]).into_owned() +} + +/// Highlights `src`, returning the HTML output. Returns only the inner html to +/// be inserted into an element. C.f., `render_with_highlighting` which includes +/// an enclosing `<pre>` block. +pub fn render_inner_with_highlighting(src: &str) -> String { + let sess = parse::ParseSess::new(); + let fm = sess.codemap().new_filemap("<stdin>".to_string(), src.to_string()); + + let mut out = Vec::new(); + write_source(&sess, + lexer::StringReader::new(&sess.span_diagnostic, fm), + &mut out).unwrap(); String::from_utf8_lossy(&out[..]).into_owned() } @@ -43,17 +57,10 @@ pub fn highlight(src: &str, class: Option<&str>, id: Option<&str>) -> String { /// it's used. All source code emission is done as slices from the source map, /// not from the tokens themselves, in order to stay true to the original /// source. -fn doit(sess: &parse::ParseSess, mut lexer: lexer::StringReader, - class: Option<&str>, id: Option<&str>, - out: &mut Write) -> io::Result<()> { - use syntax::parse::lexer::Reader; - - write!(out, "<pre ")?; - match id { - Some(id) => write!(out, "id='{}' ", id)?, - None => {} - } - write!(out, "class='rust {}'>\n", class.unwrap_or(""))?; +fn write_source(sess: &parse::ParseSess, + mut lexer: lexer::StringReader, + out: &mut Write) + -> io::Result<()> { let mut is_attribute = false; let mut is_macro = false; let mut is_macro_nonterminal = false; @@ -184,5 +191,21 @@ fn doit(sess: &parse::ParseSess, mut lexer: lexer::StringReader, } } + Ok(()) +} + +fn write_header(class: Option<&str>, + id: Option<&str>, + out: &mut Write) + -> io::Result<()> { + write!(out, "<pre ")?; + match id { + Some(id) => write!(out, "id='{}' ", id)?, + None => {} + } + write!(out, "class='rust {}'>\n", class.unwrap_or("")) +} + +fn write_footer(out: &mut Write) -> io::Result<()> { write!(out, "</pre>\n") } diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs index afc93f41172..74f7b099044 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/html/item_type.rs @@ -44,7 +44,12 @@ pub enum ItemType { impl ItemType { pub fn from_item(item: &clean::Item) -> ItemType { - match item.inner { + let inner = match item.inner { + clean::StrippedItem(box ref item) => item, + ref inner@_ => inner, + }; + + match *inner { clean::ModuleItem(..) => ItemType::Module, clean::ExternCrateItem(..) => ItemType::ExternCrate, clean::ImportItem(..) => ItemType::Import, @@ -67,6 +72,7 @@ impl ItemType { clean::AssociatedConstItem(..) => ItemType::AssociatedConst, clean::AssociatedTypeItem(..) => ItemType::AssociatedType, clean::DefaultImplItem(..) => ItemType::Impl, + clean::StrippedItem(..) => unreachable!(), } } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index e7304b69510..3baf22b38ef 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -262,9 +262,9 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { &Default::default()); s.push_str(&format!("<span class='rusttest'>{}</span>", Escape(&test))); }); - s.push_str(&highlight::highlight(&text, - Some("rust-example-rendered"), - None)); + s.push_str(&highlight::render_with_highlighting(&text, + Some("rust-example-rendered"), + None)); let output = CString::new(s).unwrap(); hoedown_buffer_puts(ob, output.as_ptr()); }) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 0f436efd70b..1427dfcbaf1 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -91,12 +91,20 @@ pub struct Context { /// String representation of how to get back to the root path of the 'doc/' /// folder in terms of a relative URL. pub root_path: String, - /// The path to the crate root source minus the file name. - /// Used for simplifying paths to the highlighted source code files. - pub src_root: PathBuf, /// The current destination folder of where HTML artifacts should be placed. /// This changes as the context descends into the module hierarchy. pub dst: PathBuf, + /// A flag, which when `true`, will render pages which redirect to the + /// real location of an item. This is used to allow external links to + /// publicly reused items to redirect to the right location. + pub render_redirect_pages: bool, + pub shared: Arc<SharedContext>, +} + +pub struct SharedContext { + /// The path to the crate root source minus the file name. + /// Used for simplifying paths to the highlighted source code files. + pub src_root: PathBuf, /// This describes the layout of each page, and is not modified after /// creation of the context (contains info like the favicon and added html). pub layout: layout::Layout, @@ -106,10 +114,6 @@ pub struct Context { pub include_sources: bool, /// The local file sources we've emitted and their respective url-paths. pub local_sources: HashMap<PathBuf, String>, - /// A flag, which when turned off, will render pages which redirect to the - /// real location of an item. This is used to allow external links to - /// publicly reused items to redirect to the right location. - pub render_redirect_pages: bool, /// All the passes that were run on this crate. pub passes: HashSet<String>, /// The base-URL of the issue tracker for when an item has been tagged with @@ -245,8 +249,7 @@ pub struct Cache { parent_stack: Vec<DefId>, parent_is_trait_impl: bool, search_index: Vec<IndexItem>, - privmod: bool, - remove_priv: bool, + stripped_mod: bool, access_levels: AccessLevels<DefId>, deref_trait_did: Option<DefId>, @@ -260,7 +263,7 @@ pub struct Cache { /// Helper struct to render all source code to HTML pages struct SourceCollector<'a> { - cx: &'a mut Context, + scx: &'a mut SharedContext, /// Root destination to place all HTML output into dst: PathBuf, @@ -413,12 +416,12 @@ pub fn run(mut krate: clean::Crate, Some(p) => p.to_path_buf(), None => PathBuf::new(), }; - let mut cx = Context { - dst: dst, + let mut scx = SharedContext { src_root: src_root, passes: passes, - current: Vec::new(), - root_path: String::new(), + include_sources: true, + local_sources: HashMap::new(), + issue_tracker_base_url: None, layout: layout::Layout { logo: "".to_string(), favicon: "".to_string(), @@ -426,14 +429,8 @@ pub fn run(mut krate: clean::Crate, krate: krate.name.clone(), playground_url: "".to_string(), }, - include_sources: true, - local_sources: HashMap::new(), - render_redirect_pages: false, - issue_tracker_base_url: None, }; - try_err!(mkdir(&cx.dst), &cx.dst); - // Crawl the crate attributes looking for attributes which control how we're // going to emit HTML if let Some(attrs) = krate.module.as_ref().map(|m| m.attrs.list("doc")) { @@ -441,15 +438,15 @@ pub fn run(mut krate: clean::Crate, match *attr { clean::NameValue(ref x, ref s) if "html_favicon_url" == *x => { - cx.layout.favicon = s.to_string(); + scx.layout.favicon = s.to_string(); } clean::NameValue(ref x, ref s) if "html_logo_url" == *x => { - cx.layout.logo = s.to_string(); + scx.layout.logo = s.to_string(); } clean::NameValue(ref x, ref s) if "html_playground_url" == *x => { - cx.layout.playground_url = s.to_string(); + scx.layout.playground_url = s.to_string(); markdown::PLAYGROUND_KRATE.with(|slot| { if slot.borrow().is_none() { let name = krate.name.clone(); @@ -459,16 +456,25 @@ pub fn run(mut krate: clean::Crate, } clean::NameValue(ref x, ref s) if "issue_tracker_base_url" == *x => { - cx.issue_tracker_base_url = Some(s.to_string()); + scx.issue_tracker_base_url = Some(s.to_string()); } clean::Word(ref x) if "html_no_source" == *x => { - cx.include_sources = false; + scx.include_sources = false; } _ => {} } } } + try_err!(mkdir(&dst), &dst); + krate = render_sources(&dst, &mut scx, krate)?; + let cx = Context { + current: Vec::new(), + root_path: String::new(), + dst: dst, + render_redirect_pages: false, + shared: Arc::new(scx), + }; // Crawl the crate to build various caches used for the output let analysis = ::ANALYSISKEY.with(|a| a.clone()); @@ -492,8 +498,7 @@ pub fn run(mut krate: clean::Crate, parent_is_trait_impl: false, extern_locations: HashMap::new(), primitive_locations: HashMap::new(), - remove_priv: cx.passes.contains("strip-private"), - privmod: false, + stripped_mod: false, access_levels: access_levels, orphan_methods: Vec::new(), traits: mem::replace(&mut krate.external_traits, HashMap::new()), @@ -540,7 +545,6 @@ pub fn run(mut krate: clean::Crate, CURRENT_LOCATION_KEY.with(|s| s.borrow_mut().clear()); write_shared(&cx, &krate, &*cache, index)?; - let krate = render_sources(&mut cx, krate)?; // And finally render the whole crate's documentation cx.krate(krate) @@ -762,16 +766,16 @@ fn write_shared(cx: &Context, Ok(()) } -fn render_sources(cx: &mut Context, +fn render_sources(dst: &Path, scx: &mut SharedContext, krate: clean::Crate) -> Result<clean::Crate, Error> { info!("emitting source files"); - let dst = cx.dst.join("src"); + let dst = dst.join("src"); try_err!(mkdir(&dst), &dst); let dst = dst.join(&krate.name); try_err!(mkdir(&dst), &dst); let mut folder = SourceCollector { dst: dst, - cx: cx, + scx: scx, }; Ok(folder.fold_crate(krate)) } @@ -849,7 +853,7 @@ impl<'a> DocFolder for SourceCollector<'a> { fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { // If we're including source files, and we haven't seen this file yet, // then we need to render it out to the filesystem - if self.cx.include_sources + if self.scx.include_sources // skip all invalid spans && item.source.filename != "" // macros from other libraries get special filenames which we can @@ -862,7 +866,7 @@ impl<'a> DocFolder for SourceCollector<'a> { // something like that), so just don't include sources for the // entire crate. The other option is maintaining this mapping on a // per-file basis, but that's probably not worth it... - self.cx + self.scx .include_sources = match self.emit_source(&item.source.filename) { Ok(()) => true, Err(e) => { @@ -874,7 +878,6 @@ impl<'a> DocFolder for SourceCollector<'a> { } }; } - self.fold_item_recur(item) } } @@ -883,7 +886,7 @@ impl<'a> SourceCollector<'a> { /// Renders the given filename into its corresponding HTML source file. fn emit_source(&mut self, filename: &str) -> io::Result<()> { let p = PathBuf::from(filename); - if self.cx.local_sources.contains_key(&p) { + if self.scx.local_sources.contains_key(&p) { // We've already emitted this source return Ok(()); } @@ -904,7 +907,7 @@ impl<'a> SourceCollector<'a> { let mut cur = self.dst.clone(); let mut root_path = String::from("../../"); let mut href = String::new(); - clean_srcpath(&self.cx.src_root, &p, false, |component| { + clean_srcpath(&self.scx.src_root, &p, false, |component| { cur.push(component); mkdir(&cur).unwrap(); root_path.push_str("../"); @@ -928,24 +931,25 @@ impl<'a> SourceCollector<'a> { description: &desc, keywords: BASIC_KEYWORDS, }; - layout::render(&mut w, &self.cx.layout, + layout::render(&mut w, &self.scx.layout, &page, &(""), &Source(contents))?; w.flush()?; - self.cx.local_sources.insert(p, href); + self.scx.local_sources.insert(p, href); Ok(()) } } impl DocFolder for Cache { fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { - // If this is a private module, we don't want it in the search index. - let orig_privmod = match item.inner { - clean::ModuleItem(..) => { - let prev = self.privmod; - self.privmod = prev || (self.remove_priv && item.visibility != Some(hir::Public)); + // If this is a stripped module, + // we don't want it or its children in the search index. + let orig_stripped_mod = match item.inner { + clean::StrippedItem(box clean::ModuleItem(..)) => { + let prev = self.stripped_mod; + self.stripped_mod = true; prev } - _ => self.privmod, + _ => self.stripped_mod, }; // Register any generics to their corresponding string. This is used @@ -983,6 +987,7 @@ impl DocFolder for Cache { // Index this method for searching later on if let Some(ref s) = item.name { let (parent, is_method) = match item.inner { + clean::StrippedItem(..) => ((None, None), false), clean::AssociatedConstItem(..) | clean::TypedefItem(_, true) if self.parent_is_trait_impl => { // skip associated items in trait impls @@ -1021,13 +1026,9 @@ impl DocFolder for Cache { } _ => ((None, Some(&*self.stack)), false) }; - let hidden_field = match item.inner { - clean::StructFieldItem(clean::HiddenStructField) => true, - _ => false - }; match parent { - (parent, Some(path)) if is_method || (!self.privmod && !hidden_field) => { + (parent, Some(path)) if is_method || (!self.stripped_mod) => { // Needed to determine `self` type. let parent_basename = self.parent_stack.first().and_then(|parent| { match self.paths.get(parent) { @@ -1035,6 +1036,7 @@ impl DocFolder for Cache { _ => None } }); + debug_assert!(!item.is_stripped()); // A crate has a module at its root, containing all items, // which should not be indexed. The crate-item itself is @@ -1051,7 +1053,7 @@ impl DocFolder for Cache { }); } } - (Some(parent), None) if is_method || (!self.privmod && !hidden_field)=> { + (Some(parent), None) if is_method || (!self.stripped_mod)=> { if parent.is_local() { // We have a parent, but we don't know where they're // defined yet. Wait for later to index this item. @@ -1075,7 +1077,7 @@ impl DocFolder for Cache { clean::StructItem(..) | clean::EnumItem(..) | clean::TypedefItem(..) | clean::TraitItem(..) | clean::FunctionItem(..) | clean::ModuleItem(..) | - clean::ForeignFunctionItem(..) if !self.privmod => { + clean::ForeignFunctionItem(..) if !self.stripped_mod => { // Reexported items mean that the same id can show up twice // in the rustdoc ast that we're looking at. We know, // however, that a reexported item doesn't show up in the @@ -1093,7 +1095,7 @@ impl DocFolder for Cache { } // link variants to their parent enum because pages aren't emitted // for each variant - clean::VariantItem(..) if !self.privmod => { + clean::VariantItem(..) if !self.stripped_mod => { let mut stack = self.stack.clone(); stack.pop(); self.paths.insert(item.def_id, (stack, ItemType::Enum)); @@ -1176,7 +1178,7 @@ impl DocFolder for Cache { if pushed { self.stack.pop().unwrap(); } if parent_pushed { self.parent_stack.pop().unwrap(); } - self.privmod = orig_privmod; + self.stripped_mod = orig_stripped_mod; self.parent_is_trait_impl = orig_parent_is_trait_impl; return ret; } @@ -1233,15 +1235,12 @@ impl Context { // render the crate documentation let mut work = vec!((self, item)); - loop { - match work.pop() { - Some((mut cx, item)) => cx.item(item, |cx, item| { - work.push((cx.clone(), item)); - })?, - None => break, - } - } + while let Some((mut cx, item)) = work.pop() { + cx.item(item, |cx, item| { + work.push((cx.clone(), item)) + })? + } Ok(()) } @@ -1272,10 +1271,10 @@ impl Context { let tyname = shortty(it).to_static_str(); let desc = if it.is_crate() { format!("API documentation for the Rust `{}` crate.", - cx.layout.krate) + cx.shared.layout.krate) } else { format!("API documentation for the Rust `{}` {} in crate `{}`.", - it.name.as_ref().unwrap(), tyname, cx.layout.krate) + it.name.as_ref().unwrap(), tyname, cx.shared.layout.krate) }; let keywords = make_item_keywords(it); let page = layout::Page { @@ -1293,82 +1292,75 @@ impl Context { // write syscall all the time. let mut writer = BufWriter::new(w); if !cx.render_redirect_pages { - layout::render(&mut writer, &cx.layout, &page, + layout::render(&mut writer, &cx.shared.layout, &page, &Sidebar{ cx: cx, item: it }, &Item{ cx: cx, item: it })?; + } else { let mut url = repeat("../").take(cx.current.len()) .collect::<String>(); - match cache().paths.get(&it.def_id) { - Some(&(ref names, _)) => { - for name in &names[..names.len() - 1] { - url.push_str(name); - url.push_str("/"); - } - url.push_str(&item_path(it)); - layout::redirect(&mut writer, &url)?; + if let Some(&(ref names, _)) = cache().paths.get(&it.def_id) { + for name in &names[..names.len() - 1] { + url.push_str(name); + url.push_str("/"); } - None => {} + url.push_str(&item_path(it)); + layout::redirect(&mut writer, &url)?; } } writer.flush() } - // Private modules may survive the strip-private pass if they - // contain impls for public types. These modules can also + // Stripped modules survive the rustdoc passes (i.e. `strip-private`) + // if they contain impls for public types. These modules can also // contain items such as publicly reexported structures. // // External crates will provide links to these structures, so - // these modules are recursed into, but not rendered normally (a - // flag on the context). + // these modules are recursed into, but not rendered normally + // (a flag on the context). if !self.render_redirect_pages { - self.render_redirect_pages = self.ignore_private_item(&item); + self.render_redirect_pages = self.maybe_ignore_item(&item); } - match item.inner { + if item.is_mod() { // modules are special because they add a namespace. We also need to // recurse into the items of the module as well. - clean::ModuleItem(..) => { - let name = item.name.as_ref().unwrap().to_string(); - let mut item = Some(item); - self.recurse(name, |this| { - let item = item.take().unwrap(); - let joint_dst = this.dst.join("index.html"); - let dst = try_err!(File::create(&joint_dst), &joint_dst); - try_err!(render(dst, this, &item, false), &joint_dst); - - let m = match item.inner { - clean::ModuleItem(m) => m, - _ => unreachable!() - }; - - // render sidebar-items.js used throughout this module - { - let items = this.build_sidebar_items(&m); - let js_dst = this.dst.join("sidebar-items.js"); - let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst)); - try_err!(write!(&mut js_out, "initSidebarItems({});", - as_json(&items)), &js_dst); - } + let name = item.name.as_ref().unwrap().to_string(); + let mut item = Some(item); + self.recurse(name, |this| { + let item = item.take().unwrap(); + let joint_dst = this.dst.join("index.html"); + let dst = try_err!(File::create(&joint_dst), &joint_dst); + try_err!(render(dst, this, &item, false), &joint_dst); - for item in m.items { - f(this,item); - } - Ok(()) - }) - } + let m = match item.inner { + clean::StrippedItem(box clean::ModuleItem(m)) | + clean::ModuleItem(m) => m, + _ => unreachable!() + }; - // Things which don't have names (like impls) don't get special - // pages dedicated to them. - _ if item.name.is_some() => { - let joint_dst = self.dst.join(&item_path(&item)); + // render sidebar-items.js used throughout this module + { + let items = this.build_sidebar_items(&m); + let js_dst = this.dst.join("sidebar-items.js"); + let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst)); + try_err!(write!(&mut js_out, "initSidebarItems({});", + as_json(&items)), &js_dst); + } - let dst = try_err!(File::create(&joint_dst), &joint_dst); - try_err!(render(dst, self, &item, true), &joint_dst); + for item in m.items { + f(this,item); + } Ok(()) - } + }) + } else if item.name.is_some() { + let joint_dst = self.dst.join(&item_path(&item)); - _ => Ok(()) + let dst = try_err!(File::create(&joint_dst), &joint_dst); + try_err!(render(dst, self, &item, true), &joint_dst); + Ok(()) + } else { + Ok(()) } } @@ -1376,7 +1368,7 @@ impl Context { // BTreeMap instead of HashMap to get a sorted output let mut map = BTreeMap::new(); for item in &m.items { - if self.ignore_private_item(item) { continue } + if self.maybe_ignore_item(item) { continue } let short = shortty(item).to_static_str(); let myname = match item.name { @@ -1394,27 +1386,18 @@ impl Context { return map; } - fn ignore_private_item(&self, it: &clean::Item) -> bool { + fn maybe_ignore_item(&self, it: &clean::Item) -> bool { match it.inner { + clean::StrippedItem(..) => true, clean::ModuleItem(ref m) => { - (m.items.is_empty() && - it.doc_value().is_none() && - it.visibility != Some(hir::Public)) || - (self.passes.contains("strip-private") && it.visibility != Some(hir::Public)) - } - clean::PrimitiveItem(..) => it.visibility != Some(hir::Public), + it.doc_value().is_none() && m.items.is_empty() && it.visibility != Some(hir::Public) + }, _ => false, } } } impl<'a> Item<'a> { - fn ismodule(&self) -> bool { - match self.item.inner { - clean::ModuleItem(..) => true, _ => false - } - } - /// Generate a url appropriate for an `href` attribute back to the source of /// this item. /// @@ -1457,10 +1440,11 @@ impl<'a> Item<'a> { // know the span, so we plow forward and generate a proper url. The url // has anchors for the line numbers that we're linking to. } else if self.item.def_id.is_local() { - self.cx.local_sources.get(&PathBuf::from(&self.item.source.filename)).map(|path| { + let path = PathBuf::from(&self.item.source.filename); + self.cx.shared.local_sources.get(&path).map(|path| { format!("{root}src/{krate}/{path}#{href}", root = self.cx.root_path, - krate = self.cx.layout.krate, + krate = self.cx.shared.layout.krate, path = path, href = href) }) @@ -1495,6 +1479,7 @@ impl<'a> Item<'a> { impl<'a> fmt::Display for Item<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + debug_assert!(!self.item.is_stripped()); // Write the breadcrumb trail header for the top write!(fmt, "\n<h1 class='fqn'><span class='in-band'>")?; match self.item.inner { @@ -1516,7 +1501,7 @@ impl<'a> fmt::Display for Item<'a> { }; if !is_primitive { let cur = &self.cx.current; - let amt = if self.ismodule() { cur.len() - 1 } else { cur.len() }; + let amt = if self.item.is_mod() { cur.len() - 1 } else { cur.len() }; for (i, component) in cur.iter().enumerate().take(amt) { write!(fmt, "<a href='{}index.html'>{}</a>::<wbr>", repeat("../").take(cur.len() - i - 1) @@ -1542,7 +1527,7 @@ impl<'a> fmt::Display for Item<'a> { // [src] link in the downstream documentation will actually come back to // this page, and this link will be auto-clicked. The `id` attribute is // used to find the link to auto-click. - if self.cx.include_sources && !is_primitive { + if self.cx.shared.include_sources && !is_primitive { if let Some(l) = self.href() { write!(fmt, "<a id='src-{}' class='srclink' \ href='{}' title='{}'>[src]</a>", @@ -1575,15 +1560,12 @@ impl<'a> fmt::Display for Item<'a> { } fn item_path(item: &clean::Item) -> String { - match item.inner { - clean::ModuleItem(..) => { - format!("{}/index.html", item.name.as_ref().unwrap()) - } - _ => { - format!("{}.{}.html", - shortty(item).to_static_str(), - *item.name.as_ref().unwrap()) - } + if item.is_mod() { + format!("{}/index.html", item.name.as_ref().unwrap()) + } else { + format!("{}.{}.html", + shortty(item).to_static_str(), + *item.name.as_ref().unwrap()) } } @@ -1626,7 +1608,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, document(w, cx, item)?; let mut indices = (0..items.len()).filter(|i| { - !cx.ignore_private_item(&items[*i]) + !cx.maybe_ignore_item(&items[*i]) }).collect::<Vec<usize>>(); // the order of item types in the listing @@ -1670,6 +1652,9 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, let mut curty = None; for &idx in &indices { let myitem = &items[idx]; + if myitem.is_stripped() { + continue; + } let myty = Some(shortty(myitem)); if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) { @@ -1774,7 +1759,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Optio format!("Deprecated{}{}", since, Markdown(&reason)) } else if stab.level == stability::Unstable { let unstable_extra = if show_reason { - match (!stab.feature.is_empty(), &cx.issue_tracker_base_url, stab.issue) { + match (!stab.feature.is_empty(), &cx.shared.issue_tracker_base_url, stab.issue) { (true, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 => format!(" (<code>{}</code> <a href=\"{}{}\">#{}</a>)", Escape(&stab.feature), tracker_url, issue_no, issue_no), @@ -2146,6 +2131,7 @@ fn render_assoc_item(w: &mut fmt::Formatter, where_clause = WhereClause(g)) } match item.inner { + clean::StrippedItem(..) => Ok(()), clean::TyMethodItem(ref m) => { method(w, item, m.unsafety, hir::Constness::NotConst, m.abi, &m.generics, &m.self_, &m.decl, link) @@ -2182,8 +2168,7 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, document(w, cx, it)?; let mut fields = s.fields.iter().filter(|f| { match f.inner { - clean::StructFieldItem(clean::HiddenStructField) => false, - clean::StructFieldItem(clean::TypedStructField(..)) => true, + clean::StructFieldItem(..) => true, _ => false, } }).peekable(); @@ -2273,7 +2258,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, if let clean::VariantItem( Variant { kind: StructVariant(ref s) } ) = variant.inner { let fields = s.fields.iter().filter(|f| { match f.inner { - clean::StructFieldItem(clean::TypedStructField(..)) => true, + clean::StructFieldItem(..) => true, _ => false, } }); @@ -2332,24 +2317,17 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item, match ty { doctree::Plain => { write!(w, " {{\n{}", tab)?; - let mut fields_stripped = false; for field in fields { - match field.inner { - clean::StructFieldItem(clean::HiddenStructField) => { - fields_stripped = true; - } - clean::StructFieldItem(clean::TypedStructField(ref ty)) => { - write!(w, " {}{}: {},\n{}", - VisSpace(field.visibility), - field.name.as_ref().unwrap(), - *ty, - tab)?; - } - _ => unreachable!(), - }; + if let clean::StructFieldItem(ref ty) = field.inner { + write!(w, " {}{}: {},\n{}", + VisSpace(field.visibility), + field.name.as_ref().unwrap(), + *ty, + tab)?; + } } - if fields_stripped { + if it.has_stripped_fields().unwrap() { write!(w, " // some fields omitted\n{}", tab)?; } write!(w, "}}")?; @@ -2361,10 +2339,10 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item, write!(w, ", ")?; } match field.inner { - clean::StructFieldItem(clean::HiddenStructField) => { + clean::StrippedItem(box clean::StructFieldItem(..)) => { write!(w, "_")? } - clean::StructFieldItem(clean::TypedStructField(ref ty)) => { + clean::StructFieldItem(ref ty) => { write!(w, "{}{}", VisSpace(field.visibility), *ty)? } _ => unreachable!() @@ -2540,6 +2518,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi assoc_type(w, item, bounds, default.as_ref(), link)?; write!(w, "</code></h4>\n")?; } + clean::StrippedItem(..) => return Ok(()), _ => panic!("can't make docs for trait item with name {:?}", item.name) } @@ -2661,16 +2640,16 @@ impl<'a> fmt::Display for Source<'a> { write!(fmt, "<span id=\"{0}\">{0:1$}</span>\n", i, cols)?; } write!(fmt, "</pre>")?; - write!(fmt, "{}", highlight::highlight(s, None, None))?; + write!(fmt, "{}", highlight::render_with_highlighting(s, None, None))?; Ok(()) } } fn item_macro(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, t: &clean::Macro) -> fmt::Result { - w.write_str(&highlight::highlight(&t.source, - Some("macro"), - None))?; + w.write_str(&highlight::render_with_highlighting(&t.source, + Some("macro"), + None))?; render_stability_since_raw(w, it.stable_since(), None)?; document(w, cx, it) } diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index 88cb20991d6..f93ecb46228 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -21,6 +21,7 @@ use clean::Item; use plugins; use fold; use fold::DocFolder; +use fold::FoldItem::Strip; /// Strip items marked `#[doc(hidden)]` pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult { @@ -39,18 +40,12 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult { // use a dedicated hidden item for given item type if any match i.inner { - clean::StructFieldItem(..) => { - return Some(clean::Item { - inner: clean::StructFieldItem(clean::HiddenStructField), - ..i - }); - } - _ => { - return None; + clean::StructFieldItem(..) | clean::ModuleItem(..) => { + return Strip(i).fold() } + _ => return None, } } - self.fold_item_recur(i) } } @@ -125,12 +120,14 @@ struct Stripper<'a> { impl<'a> fold::DocFolder for Stripper<'a> { fn fold_item(&mut self, i: Item) -> Option<Item> { match i.inner { + clean::StrippedItem(..) => return Some(i), // These items can all get re-exported clean::TypedefItem(..) | clean::StaticItem(..) | clean::StructItem(..) | clean::EnumItem(..) | clean::TraitItem(..) | clean::FunctionItem(..) | clean::VariantItem(..) | clean::MethodItem(..) | - clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => { + clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | + clean::ConstantItem(..) => { if i.def_id.is_local() { if !self.access_levels.is_exported(i.def_id) { return None; @@ -138,23 +135,17 @@ impl<'a> fold::DocFolder for Stripper<'a> { } } - clean::ConstantItem(..) => { - if i.def_id.is_local() && !self.access_levels.is_exported(i.def_id) { - return None; - } - } - clean::StructFieldItem(..) => { if i.visibility != Some(hir::Public) { - return Some(clean::Item { - inner: clean::StructFieldItem(clean::HiddenStructField), - ..i - }) + return Strip(i).fold(); } } - // handled below - clean::ModuleItem(..) => {} + clean::ModuleItem(..) => { + if i.def_id.is_local() && i.visibility != Some(hir::Public) { + return Strip(self.fold_item_recur(i).unwrap()).fold() + } + } // trait impls for private items should be stripped clean::ImplItem(clean::Impl{ @@ -165,7 +156,7 @@ impl<'a> fold::DocFolder for Stripper<'a> { } } // handled in the `strip-priv-imports` pass - clean::ExternCrateItem(..) | clean::ImportItem(_) => {} + clean::ExternCrateItem(..) | clean::ImportItem(..) => {} clean::DefaultImplItem(..) | clean::ImplItem(..) => {} @@ -187,7 +178,6 @@ impl<'a> fold::DocFolder for Stripper<'a> { // implementations of traits are always public. clean::ImplItem(ref imp) if imp.trait_.is_some() => true, - // Struct variant fields have inherited visibility clean::VariantItem(clean::Variant { kind: clean::StructVariant(..) @@ -202,19 +192,17 @@ impl<'a> fold::DocFolder for Stripper<'a> { self.fold_item_recur(i) }; - i.and_then(|i| { - match i.inner { - // emptied modules/impls have no need to exist - clean::ModuleItem(ref m) - if m.items.is_empty() && - i.doc_value().is_none() => None, - clean::ImplItem(ref i) if i.items.is_empty() => None, - _ => { - self.retained.insert(i.def_id); - Some(i) - } + i.and_then(|i| { match i.inner { + // emptied modules/impls have no need to exist + clean::ModuleItem(ref m) + if m.items.is_empty() && + i.doc_value().is_none() => None, + clean::ImplItem(ref i) if i.items.is_empty() => None, + _ => { + self.retained.insert(i.def_id); + Some(i) } - }) + }}) } } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 331e3431cee..5bd3b9c4f59 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -431,7 +431,7 @@ impl Collector { // compiler failures are test failures should_panic: testing::ShouldPanic::No, }, - testfn: testing::DynTestFn(Box::new(move|| { + testfn: testing::DynTestFn(box move|| { runtest(&test, &cratename, cfgs, @@ -442,7 +442,7 @@ impl Collector { as_test_harness, compile_fail, &opts); - })) + }) }); } diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index c332d6035ee..f8b2d4dd232 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -85,12 +85,46 @@ pub fn init() { #[cfg(not(target_os = "nacl"))] unsafe fn reset_sigpipe() { - assert!(libc::signal(libc::SIGPIPE, libc::SIG_IGN) != !0); + assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0); } #[cfg(target_os = "nacl")] unsafe fn reset_sigpipe() {} } +// Currently the minimum supported Android version of the standard library is +// API level 18 (android-18). Back in those days [1] the `signal` function was +// just an inline wrapper around `bsd_signal`, but starting in API level +// android-20 the `signal` symbols was introduced [2]. Finally, in android-21 +// the API `bsd_signal` was removed [3]. +// +// Basically this means that if we want to be binary compatible with multiple +// Android releases (oldest being 18 and newest being 21) then we need to check +// for both symbols and not actually link against either. +// +// Note that if we're not on android we just link against the `android` symbol +// itself. +// +// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms +// /android-18/arch-arm/usr/include/signal.h +// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental +// /platforms/android-20/arch-arm +// /usr/include/signal.h +// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms +// /android-21/arch-arm/usr/include/signal.h +#[cfg(target_os = "android")] +unsafe fn signal(signum: libc::c_int, + handler: libc::sighandler_t) -> libc::sighandler_t { + weak!(fn signal(libc::c_int, libc::sighandler_t) -> libc::sighandler_t); + weak!(fn bsd_signal(libc::c_int, libc::sighandler_t) -> libc::sighandler_t); + + let f = signal.get().or_else(|| bsd_signal.get()); + let f = f.expect("neither `signal` nor `bsd_signal` symbols found"); + f(signum, handler) +} + +#[cfg(not(target_os = "android"))] +pub use libc::signal; + pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno as libc::c_int { libc::ECONNREFUSED => ErrorKind::ConnectionRefused, diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 6f56f3ade06..270c2096b2c 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -393,7 +393,7 @@ impl Command { t!(cvt(libc::sigemptyset(&mut set))); t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, ptr::null_mut()))); - let ret = libc::signal(libc::SIGPIPE, libc::SIG_DFL); + let ret = super::signal(libc::SIGPIPE, libc::SIG_DFL); if ret == libc::SIG_ERR { return io::Error::last_os_error() } diff --git a/src/libstd/sys/unix/weak.rs b/src/libstd/sys/unix/weak.rs index e6f85c08d12..99ab8741159 100644 --- a/src/libstd/sys/unix/weak.rs +++ b/src/libstd/sys/unix/weak.rs @@ -75,11 +75,5 @@ unsafe fn fetch(name: &str) -> usize { Ok(cstr) => cstr, Err(..) => return 0, }; - let lib = libc::dlopen(0 as *const _, libc::RTLD_LAZY); - if lib.is_null() { - return 0 - } - let ret = libc::dlsym(lib, name.as_ptr()) as usize; - libc::dlclose(lib); - return ret + libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize } diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 945eb6a42e5..8a50f07e6d8 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -52,6 +52,7 @@ impl Duration { /// If the nanoseconds is greater than 1 billion (the number of nanoseconds /// in a second), then it will carry over into the seconds provided. #[stable(feature = "duration", since = "1.3.0")] + #[inline] pub fn new(secs: u64, nanos: u32) -> Duration { let secs = secs + (nanos / NANOS_PER_SEC) as u64; let nanos = nanos % NANOS_PER_SEC; @@ -60,12 +61,14 @@ impl Duration { /// Creates a new `Duration` from the specified number of seconds. #[stable(feature = "duration", since = "1.3.0")] + #[inline] pub fn from_secs(secs: u64) -> Duration { Duration { secs: secs, nanos: 0 } } /// Creates a new `Duration` from the specified number of milliseconds. #[stable(feature = "duration", since = "1.3.0")] + #[inline] pub fn from_millis(millis: u64) -> Duration { let secs = millis / MILLIS_PER_SEC; let nanos = ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI; @@ -77,6 +80,7 @@ impl Duration { /// The extra precision represented by this duration is ignored (e.g. extra /// nanoseconds are not represented in the returned value). #[stable(feature = "duration", since = "1.3.0")] + #[inline] pub fn as_secs(&self) -> u64 { self.secs } /// Returns the nanosecond precision represented by this duration. @@ -85,6 +89,7 @@ impl Duration { /// represented by nanoseconds. The returned number always represents a /// fractional portion of a second (e.g. it is less than one billion). #[stable(feature = "duration", since = "1.3.0")] + #[inline] pub fn subsec_nanos(&self) -> u32 { self.nanos } } diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs index 212a54447a8..f369582bc5c 100644 --- a/src/libsyntax/errors/json.rs +++ b/src/libsyntax/errors/json.rs @@ -20,7 +20,7 @@ // FIXME spec the JSON output properly. -use codemap::{Span, MultiSpan, CodeMap}; +use codemap::{self, Span, MultiSpan, CodeMap}; use diagnostics::registry::Registry; use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion}; use errors::emitter::Emitter; @@ -197,8 +197,8 @@ impl DiagnosticSpan { fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> { match *rsp { - // FIXME(#30701) handle Suggestion properly RenderSpan::FullSpan(ref msp) | + // FIXME(#30701) handle Suggestion properly RenderSpan::Suggestion(CodeSuggestion { ref msp, .. }) => { DiagnosticSpan::from_multispan(msp, je) } @@ -207,13 +207,13 @@ impl DiagnosticSpan { let end = je.cm.lookup_char_pos(span.hi); DiagnosticSpan { file_name: end.file.name.clone(), - byte_start: span.lo.0, + byte_start: span.hi.0, byte_end: span.hi.0, - line_start: 0, + line_start: end.line, line_end: end.line, - column_start: 0, + column_start: end.col.0 + 1, column_end: end.col.0 + 1, - text: DiagnosticSpanLine::from_span(span, je), + text: DiagnosticSpanLine::from_span_end(span, je), } }).collect() } @@ -237,25 +237,70 @@ impl DiagnosticSpan { } } -impl DiagnosticSpanLine { - fn from_span(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { - let lines = match je.cm.span_to_lines(*span) { +macro_rules! get_lines_for_span { + ($span: ident, $je: ident) => { + match $je.cm.span_to_lines(*$span) { Ok(lines) => lines, Err(_) => { debug!("unprintable span"); return Vec::new(); } - }; + } + } +} + +impl DiagnosticSpanLine { + fn line_from_filemap(fm: &codemap::FileMap, + index: usize, + h_start: usize, + h_end: usize) + -> DiagnosticSpanLine { + DiagnosticSpanLine { + text: fm.get_line(index).unwrap().to_owned(), + highlight_start: h_start, + highlight_end: h_end, + } + } + + /// Create a list of DiagnosticSpanLines from span - each line with any part + /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the + /// `span` within the line. + fn from_span(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { + let lines = get_lines_for_span!(span, je); let mut result = Vec::new(); let fm = &*lines.file; for line in &lines.lines { - result.push(DiagnosticSpanLine { - text: fm.get_line(line.line_index).unwrap().to_owned(), - highlight_start: line.start_col.0 + 1, - highlight_end: line.end_col.0 + 1, - }); + result.push(DiagnosticSpanLine::line_from_filemap(fm, + line.line_index, + line.start_col.0 + 1, + line.end_col.0 + 1)); + } + + result + } + + /// Create a list of DiagnosticSpanLines from span - the result covers all + /// of `span`, but the highlight is zero-length and at the end of `span`. + fn from_span_end(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { + let lines = get_lines_for_span!(span, je); + + let mut result = Vec::new(); + let fm = &*lines.file; + + for (i, line) in lines.lines.iter().enumerate() { + // Invariant - CodeMap::span_to_lines will not return extra context + // lines - the last line returned is the last line of `span`. + let highlight = if i == lines.lines.len() - 1 { + (line.end_col.0 + 1, line.end_col.0 + 1) + } else { + (0, 0) + }; + result.push(DiagnosticSpanLine::line_from_filemap(fm, + line.line_index, + highlight.0, + highlight.1)); } result diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9027a5b1074..75916b87c12 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -6124,7 +6124,7 @@ impl<'a> Parser<'a> { // Allow a leading :: because the paths are absolute either way. // This occurs with "use $crate::..." in macros. - self.eat(&token::ModSep); + let is_global = self.eat(&token::ModSep); if self.check(&token::OpenDelim(token::Brace)) { // use {foo,bar} @@ -6135,7 +6135,7 @@ impl<'a> Parser<'a> { |p| p.parse_path_list_item())?; let path = ast::Path { span: mk_sp(lo, self.span.hi), - global: false, + global: is_global, segments: Vec::new() }; return Ok(P(spanned(lo, self.span.hi, ViewPathList(path, idents)))); @@ -6164,7 +6164,7 @@ impl<'a> Parser<'a> { )?; let path = ast::Path { span: mk_sp(lo, self.span.hi), - global: false, + global: is_global, segments: path.into_iter().map(|identifier| { ast::PathSegment { identifier: identifier, @@ -6180,7 +6180,7 @@ impl<'a> Parser<'a> { self.bump(); let path = ast::Path { span: mk_sp(lo, self.span.hi), - global: false, + global: is_global, segments: path.into_iter().map(|identifier| { ast::PathSegment { identifier: identifier, @@ -6203,7 +6203,7 @@ impl<'a> Parser<'a> { let mut rename_to = path[path.len() - 1]; let path = ast::Path { span: mk_sp(lo, self.last_span.hi), - global: false, + global: is_global, segments: path.into_iter().map(|identifier| { ast::PathSegment { identifier: identifier, diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index a02a10aa003..16417ac0044 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -514,7 +514,7 @@ macro_rules! declare_special_idents_and_keywords {( // If the special idents get renumbered, remember to modify these two as appropriate pub const SELF_KEYWORD_NAME: ast::Name = ast::Name(SELF_KEYWORD_NAME_NUM); const STATIC_KEYWORD_NAME: ast::Name = ast::Name(STATIC_KEYWORD_NAME_NUM); -const SUPER_KEYWORD_NAME: ast::Name = ast::Name(SUPER_KEYWORD_NAME_NUM); +pub const SUPER_KEYWORD_NAME: ast::Name = ast::Name(SUPER_KEYWORD_NAME_NUM); const SELF_TYPE_KEYWORD_NAME: ast::Name = ast::Name(SELF_TYPE_KEYWORD_NAME_NUM); pub const SELF_KEYWORD_NAME_NUM: u32 = 1; diff --git a/src/snapshots.txt b/src/snapshots.txt index d1a2ab464cb..61dfd4f8f86 100644 --- a/src/snapshots.txt +++ b/src/snapshots.txt @@ -9,6 +9,7 @@ S 2016-03-18 235d774 freebsd-x86_64 390b9a9f60f3d0d6a52c04d939a0355f572d03b3 S 2016-02-17 4d3eebf + dragonfly-x86_64 765bb5820ad406e966ec0ac51c8070b656459b02 linux-i386 5f194aa7628c0703f0fd48adc4ec7f3cc64b98c7 linux-x86_64 d29b7607d13d64078b6324aec82926fb493f59ba macos-i386 4c8e42dd649e247f3576bf9dfa273327b4907f9c @@ -16,6 +17,7 @@ S 2016-02-17 4d3eebf winnt-i386 0c336d794a65f8e285c121866c7d59aa2dd0d1e1 winnt-x86_64 27e75b1bf99770b3564bcebd7f3230be01135a92 openbsd-x86_64 ac957c6b84de2bd67f01df085d9ea515f96e22f3 + freebsd-i386 4e2af0b34eb335e173aebff543be693724a956c2 freebsd-x86_64 f38991fbb81c1cd8d0bbda396f98f13a55b42804 S 2015-12-18 3391630 diff --git a/src/test/auxiliary/reexp_stripped.rs b/src/test/auxiliary/reexp_stripped.rs new file mode 100644 index 00000000000..2b061e3997d --- /dev/null +++ b/src/test/auxiliary/reexp_stripped.rs @@ -0,0 +1,21 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use private::Quz; +pub use hidden::Bar; + +mod private { + pub struct Quz; +} + +#[doc(hidden)] +pub mod hidden { + pub struct Bar; +} diff --git a/src/test/compile-fail/consider-removing-last-semi.rs b/src/test/compile-fail/consider-removing-last-semi.rs new file mode 100644 index 00000000000..02148a138c9 --- /dev/null +++ b/src/test/compile-fail/consider-removing-last-semi.rs @@ -0,0 +1,23 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn f() -> String { //~ ERROR E0269 + //~^ HELP detailed explanation + 0u8; + "bla".to_string(); //~ HELP consider removing this semicolon +} + +fn g() -> String { //~ ERROR E0269 + //~^ HELP detailed explanation + "this won't work".to_string(); + "removeme".to_string(); //~ HELP consider removing this semicolon +} + +fn main() {} diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs new file mode 100644 index 00000000000..cdcdb919bde --- /dev/null +++ b/src/test/compile-fail/const-err-early.rs @@ -0,0 +1,22 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_indexing)] +#![deny(const_err)] + +pub const A: i8 = -std::i8::MIN; //~ ERROR attempted to negate with overflow +pub const B: u8 = 200u8 + 200u8; //~ ERROR attempted to add with overflow +pub const C: u8 = 200u8 * 4; //~ ERROR attempted to multiply with overflow +pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR attempted to subtract with overflow +pub const E: u8 = [5u8][1]; //~ ERROR index out of bounds + +fn main() { + let _e = [6u8][1]; +} diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index 882e4cb2d47..45e8fc37d87 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -10,15 +10,11 @@ #![feature(rustc_attrs)] #![allow(exceeding_bitshifts)] -#![deny(const_err)] fn black_box<T>(_: T) { unimplemented!() } -const BLA: u8 = 200u8 + 200u8; -//~^ ERROR attempted to add with overflow - #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD. fn main() { let a = -std::i8::MIN; @@ -30,7 +26,8 @@ fn main() { //~^ WARN attempted to multiply with overflow let d = 42u8 - (42u8 + 1); //~^ WARN attempted to subtract with overflow - let _e = BLA; + let _e = [5u8][1]; + //~^ ERROR const index-expr is out of bounds black_box(a); black_box(b); black_box(c); diff --git a/src/test/compile-fail/const-eval-span.rs b/src/test/compile-fail/const-eval-span.rs index 44ab798f491..9fdd24c42fd 100644 --- a/src/test/compile-fail/const-eval-span.rs +++ b/src/test/compile-fail/const-eval-span.rs @@ -14,7 +14,7 @@ struct S(i32); const CONSTANT: S = S(0); -//~^ ERROR: constant evaluation error: call on struct [E0080] +//~^ ERROR: unimplemented constant expression: tuple struct constructors [E0080] enum E { V = CONSTANT, diff --git a/src/test/compile-fail/const-pattern-not-const-evaluable.rs b/src/test/compile-fail/const-pattern-not-const-evaluable.rs index ecc43d21a46..4567cd4a74b 100644 --- a/src/test/compile-fail/const-pattern-not-const-evaluable.rs +++ b/src/test/compile-fail/const-pattern-not-const-evaluable.rs @@ -17,12 +17,12 @@ enum Cake { use Cake::*; const BOO: (Cake, Cake) = (Marmor, BlackForest); -//~^ ERROR: constant evaluation error: non-constant path in constant expression [E0471] +//~^ ERROR: constant evaluation error: unimplemented constant expression: enum variants [E0471] const FOO: Cake = BOO.1; const fn foo() -> Cake { - Marmor //~ ERROR: constant evaluation error: non-constant path in constant expression [E0471] - //~^ ERROR: non-constant path in constant expression + Marmor //~ ERROR: constant evaluation error: unimplemented constant expression: enum variants + //~^ ERROR: unimplemented constant expression: enum variants } const WORKS: Cake = Marmor; diff --git a/src/test/compile-fail/feature-gate-negate-unsigned.rs b/src/test/compile-fail/feature-gate-negate-unsigned.rs index 4330a4cbeab..93e09c6d8d2 100644 --- a/src/test/compile-fail/feature-gate-negate-unsigned.rs +++ b/src/test/compile-fail/feature-gate-negate-unsigned.rs @@ -17,7 +17,8 @@ impl std::ops::Neg for S { } const _MAX: usize = -1; -//~^ ERROR unary negation of unsigned integer +//~^ WARN unary negation of unsigned integer +//~| ERROR unary negation of unsigned integer //~| HELP use a cast or the `!` operator fn main() { diff --git a/src/test/compile-fail/issue-32089.rs b/src/test/compile-fail/issue-32119.rs index 5da7b9fff6e..4743b779ef6 100644 --- a/src/test/compile-fail/issue-32089.rs +++ b/src/test/compile-fail/issue-32119.rs @@ -9,15 +9,20 @@ // except according to those terms. #![feature(rustc_attrs)] -#![allow(unused_imports)] -pub type Type = i32; +pub type T = (); +mod foo { pub use super::T; } +mod bar { pub use super::T; } -mod one { use super::Type; } -pub use self::one::*; +pub use foo::*; +pub use bar::*; -mod two { use super::Type; } -pub use self::two::*; +mod baz { + pub type T = (); + mod foo { pub use super::T as S; } + mod bar { pub use super::foo::S as T; } + pub use self::bar::*; +} #[rustc_error] fn main() {} //~ ERROR compilation successful diff --git a/src/test/compile-fail/issue-32128.rs b/src/test/compile-fail/issue-32128.rs new file mode 100644 index 00000000000..fe7e66a2116 --- /dev/null +++ b/src/test/compile-fail/issue-32128.rs @@ -0,0 +1,25 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Example { + example: Box<Fn(i32) -> i32> +} + +fn main() { + let demo = Example { + example: Box::new(|x| { + x + 1 + }) + }; + + demo.example(1); //~ ERROR no method named `example` + //~^ NOTE use `(demo.example)(...)` + // (demo.example)(1); +} diff --git a/src/test/compile-fail/issue-32326.rs b/src/test/compile-fail/issue-32326.rs new file mode 100644 index 00000000000..8af243afc22 --- /dev/null +++ b/src/test/compile-fail/issue-32326.rs @@ -0,0 +1,20 @@ +// Copyright 2012 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #32326. We ran out of memory because we +// attempted to expand this case up to the recursion limit, and 2^N is +// too big. + +enum Expr { //~ ERROR E0072 + Plus(Expr, Expr), + Literal(i64), +} + +fn main() { } diff --git a/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs b/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs index a7cabae16be..9564a080b8e 100644 --- a/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs +++ b/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs @@ -15,5 +15,5 @@ enum State { ST_NULL, ST_WHITESPACE } fn main() { [State::ST_NULL; (State::ST_WHITESPACE as usize)]; - //~^ ERROR expected constant integer for repeat count, but non-constant path + //~^ ERROR expected constant integer for repeat count, but unimplemented constant expression } diff --git a/src/test/compile-fail/non-interger-atomic.rs b/src/test/compile-fail/non-interger-atomic.rs new file mode 100644 index 00000000000..d2376eecd9b --- /dev/null +++ b/src/test/compile-fail/non-interger-atomic.rs @@ -0,0 +1,117 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(core_intrinsics, rustc_attrs)] +#![allow(warnings)] + +use std::intrinsics; + +#[derive(Copy, Clone)] +struct Foo(i64); +type Bar = &'static Fn(); +type Quux = [u8; 100]; + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_bool_load(p: &mut bool, v: bool) { + intrinsics::atomic_load(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `bool` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_bool_store(p: &mut bool, v: bool) { + intrinsics::atomic_store(p, v); + //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `bool` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_bool_xchg(p: &mut bool, v: bool) { + intrinsics::atomic_xchg(p, v); + //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `bool` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_bool_cxchg(p: &mut bool, v: bool) { + intrinsics::atomic_cxchg(p, v, v); + //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `bool` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Foo_load(p: &mut Foo, v: Foo) { + intrinsics::atomic_load(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `Foo` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Foo_store(p: &mut Foo, v: Foo) { + intrinsics::atomic_store(p, v); + //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `Foo` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Foo_xchg(p: &mut Foo, v: Foo) { + intrinsics::atomic_xchg(p, v); + //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `Foo` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Foo_cxchg(p: &mut Foo, v: Foo) { + intrinsics::atomic_cxchg(p, v, v); + //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `Foo` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Bar_load(p: &mut Bar, v: Bar) { + intrinsics::atomic_load(p); + //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Bar_store(p: &mut Bar, v: Bar) { + intrinsics::atomic_store(p, v); + //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Bar_xchg(p: &mut Bar, v: Bar) { + intrinsics::atomic_xchg(p, v); + //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Bar_cxchg(p: &mut Bar, v: Bar) { + intrinsics::atomic_cxchg(p, v, v); + //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Quux_load(p: &mut Quux, v: Quux) { + intrinsics::atomic_load(p); + //~^ ERROR `atomic_load` intrinsic: expected basic integer type, found `[u8; 100]` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Quux_store(p: &mut Quux, v: Quux) { + intrinsics::atomic_store(p, v); + //~^ ERROR `atomic_store` intrinsic: expected basic integer type, found `[u8; 100]` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Quux_xchg(p: &mut Quux, v: Quux) { + intrinsics::atomic_xchg(p, v); + //~^ ERROR `atomic_xchg` intrinsic: expected basic integer type, found `[u8; 100]` +} + +#[rustc_no_mir] // FIXME #27840 MIR doesn't provide precise spans for calls. +unsafe fn test_Quux_cxchg(p: &mut Quux, v: Quux) { + intrinsics::atomic_cxchg(p, v, v); + //~^ ERROR `atomic_cxchg` intrinsic: expected basic integer type, found `[u8; 100]` +} + +fn main() {} diff --git a/src/test/compile-fail/sized-cycle-note.rs b/src/test/compile-fail/sized-cycle-note.rs index ec378d05ba5..3d7c4868e96 100644 --- a/src/test/compile-fail/sized-cycle-note.rs +++ b/src/test/compile-fail/sized-cycle-note.rs @@ -20,11 +20,11 @@ struct Baz { q: Option<Foo> } struct Foo { q: Option<Baz> } //~^ ERROR recursive type `Foo` has infinite size -//~| type `Foo` is embedded within `std::option::Option<Foo>`... -//~| ...which in turn is embedded within `std::option::Option<Foo>`... -//~| ...which in turn is embedded within `Baz`... -//~| ...which in turn is embedded within `std::option::Option<Baz>`... -//~| ...which in turn is embedded within `Foo`, completing the cycle. +//~| NOTE type `Foo` is embedded within `std::option::Option<Foo>`... +//~| NOTE ...which in turn is embedded within `std::option::Option<Foo>`... +//~| NOTE ...which in turn is embedded within `Baz`... +//~| NOTE ...which in turn is embedded within `std::option::Option<Baz>`... +//~| NOTE ...which in turn is embedded within `Foo`, completing the cycle. impl Foo { fn bar(&self) {} } diff --git a/src/test/compile-fail/traits-inductive-overflow-auto-normal-auto.rs b/src/test/compile-fail/traits-inductive-overflow-auto-normal-auto.rs new file mode 100644 index 00000000000..cdf4b405fd8 --- /dev/null +++ b/src/test/compile-fail/traits-inductive-overflow-auto-normal-auto.rs @@ -0,0 +1,32 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test for a potential corner case in current impl where you have an +// auto trait (Magic1) that depends on a normal trait (Magic2) which +// in turn depends on the auto trait (Magic1). This was incorrectly +// being considered coinductive, but because of the normal trait +// interfering, it should not be. + +#![feature(optin_builtin_traits)] + +trait Magic1: Magic2 { } +impl Magic1 for .. {} + +trait Magic2 { } +impl<T: Magic1> Magic2 for T { } + +fn is_magic1<T: Magic1>() { } + +#[derive(Debug)] +struct NoClone; + +fn main() { + is_magic1::<NoClone>(); //~ ERROR E0275 +} diff --git a/src/test/compile-fail/use-super-global-path.rs b/src/test/compile-fail/use-super-global-path.rs new file mode 100644 index 00000000000..d721d428f29 --- /dev/null +++ b/src/test/compile-fail/use-super-global-path.rs @@ -0,0 +1,22 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +mod foo { + pub fn g() { + use ::super::main; //~ WARN expected identifier, found keyword `super` + //~^ WARN this was previously accepted by the compiler but is being phased out + main(); + } +} + +#[rustc_error] +fn main() { foo::g(); } //~ ERROR compilation successful diff --git a/src/test/run-pass/foreign-dupe.rs b/src/test/run-pass/foreign-dupe.rs index 11de5ac70f4..6c393ce99e3 100644 --- a/src/test/run-pass/foreign-dupe.rs +++ b/src/test/run-pass/foreign-dupe.rs @@ -31,9 +31,20 @@ mod rustrt2 { } } +mod rustrt3 { + // Different type, but same ABI (on all supported platforms). + // Ensures that we don't ICE or trigger LLVM asserts when + // importing the same symbol under different types. + // See https://github.com/rust-lang/rust/issues/32740. + extern { + pub fn rust_get_test_int() -> *const u8; + } +} + pub fn main() { unsafe { - rustrt1::rust_get_test_int(); - rustrt2::rust_get_test_int(); + let x = rustrt1::rust_get_test_int(); + assert_eq!(x, rustrt2::rust_get_test_int()); + assert_eq!(x as *const _, rustrt3::rust_get_test_int()); } } diff --git a/src/test/run-pass/issue-23550.rs b/src/test/run-pass/issue-23550.rs index 4b6d593f592..6e20662b702 100644 --- a/src/test/run-pass/issue-23550.rs +++ b/src/test/run-pass/issue-23550.rs @@ -16,24 +16,16 @@ use std::intrinsics; #[derive(Copy, Clone)] struct Wrap(i64); -// These volatile and atomic intrinsics used to cause an ICE +// These volatile intrinsics used to cause an ICE unsafe fn test_bool(p: &mut bool, v: bool) { intrinsics::volatile_load(p); intrinsics::volatile_store(p, v); - intrinsics::atomic_load(p); - intrinsics::atomic_cxchg(p, v, v); - intrinsics::atomic_store(p, v); - intrinsics::atomic_xchg(p, v); } unsafe fn test_immediate_fca(p: &mut Wrap, v: Wrap) { intrinsics::volatile_load(p); intrinsics::volatile_store(p, v); - intrinsics::atomic_load(p); - intrinsics::atomic_cxchg(p, v, v); - intrinsics::atomic_store(p, v); - intrinsics::atomic_xchg(p, v); } fn main() {} diff --git a/src/test/rustdoc/redirect.rs b/src/test/rustdoc/redirect.rs new file mode 100644 index 00000000000..98e66e8c024 --- /dev/null +++ b/src/test/rustdoc/redirect.rs @@ -0,0 +1,48 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:reexp_stripped.rs +// build-aux-docs +// ignore-cross-compile + +extern crate reexp_stripped; + +pub trait Foo {} + +// @has redirect/index.html +// @has - '//code' 'pub use reexp_stripped::Bar' +// @has - '//code/a' 'Bar' +// @has reexp_stripped/hidden/struct.Bar.html +// @has - '//p/a' '../../reexp_stripped/struct.Bar.html' +// @has 'reexp_stripped/struct.Bar.html' +#[doc(no_inline)] +pub use reexp_stripped::Bar; +impl Foo for Bar {} + +// @has redirect/index.html +// @has - '//code' 'pub use reexp_stripped::Quz' +// @has - '//code/a' 'Quz' +// @has reexp_stripped/private/struct.Quz.html +// @has - '//p/a' '../../reexp_stripped/struct.Quz.html' +// @has 'reexp_stripped/struct.Quz.html' +#[doc(no_inline)] +pub use reexp_stripped::Quz; +impl Foo for Quz {} + +mod private_no_inline { + pub struct Qux; + impl ::Foo for Qux {} +} + +// @has redirect/index.html +// @has - '//code' 'pub use private_no_inline::Qux' +// @!has - '//code/a' 'Qux' +#[doc(no_inline)] +pub use private_no_inline::Qux; diff --git a/src/test/rustdoc/structfields.rs b/src/test/rustdoc/structfields.rs new file mode 100644 index 00000000000..c4327f70728 --- /dev/null +++ b/src/test/rustdoc/structfields.rs @@ -0,0 +1,44 @@ +// Copyright 2016 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// @has structfields/struct.Foo.html +pub struct Foo { + // @has - //pre "pub a: ()" + pub a: (), + // @has - //pre "// some fields omitted" + // @!has - //pre "b: ()" + b: (), + // @!has - //pre "c: usize" + #[doc(hidden)] + c: usize, + // @has - //pre "pub d: usize" + pub d: usize, +} + +// @has structfields/struct.Bar.html +pub struct Bar { + // @has - //pre "pub a: ()" + pub a: (), + // @!has - //pre "// some fields omitted" +} + +// @has structfields/enum.Qux.html +pub enum Qux { + Quz { + // @has - //pre "a: ()" + a: (), + // @!has - //pre "b: ()" + #[doc(hidden)] + b: (), + // @has - //pre "c: usize" + c: usize, + // @has - //pre "// some fields omitted" + }, +} |
