From df5c62bed2afed6c0f1b64d04bdf4465db6f46ee Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 15 Mar 2016 06:10:01 -0400 Subject: [PATCH 1/4] refactor: use `select` inside of a probe We ought not to be affecting inference state when assembling candidates, so invoke select inside of a probe. --- src/librustc/traits/project.rs | 469 ++++++++++++++++++++------------- 1 file changed, 281 insertions(+), 188 deletions(-) diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 9abb179f28896..b6f7e19b04a56 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -152,14 +152,8 @@ enum ProjectionTyCandidate<'tcx> { // from the definition of `Trait` when you have something like <::B as Trait2>::C TraitDef(ty::PolyProjectionPredicate<'tcx>), - // defined in an impl - Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>), - - // closure return type - Closure(VtableClosureData<'tcx, PredicateObligation<'tcx>>), - - // fn pointer return type - FnPointer(VtableFnPointerData<'tcx, PredicateObligation<'tcx>>), + // from a "impl" (or a "pseudo-impl" returned by select) + Select, } struct ProjectionTyCandidateSet<'tcx> { @@ -599,10 +593,8 @@ fn project_type<'cx, 'gcx, 'tcx>( debug!("retaining param-env candidates only from {:?}", candidates.vec); candidates.vec.retain(|c| match *c { ProjectionTyCandidate::ParamEnv(..) => true, - ProjectionTyCandidate::Impl(..) | - ProjectionTyCandidate::Closure(..) | ProjectionTyCandidate::TraitDef(..) | - ProjectionTyCandidate::FnPointer(..) => false, + ProjectionTyCandidate::Select => false, }); debug!("resulting candidate set: {:?}", candidates.vec); if candidates.vec.len() != 1 { @@ -612,78 +604,12 @@ fn project_type<'cx, 'gcx, 'tcx>( assert!(candidates.vec.len() <= 1); - let possible_candidate = candidates.vec.pop().and_then(|candidate| { - // In Any (i.e. trans) mode, all projections succeed; - // otherwise, we need to be sensitive to `default` and - // specialization. - if !selcx.projection_mode().is_any() { - if let ProjectionTyCandidate::Impl(ref impl_data) = candidate { - if let Some(node_item) = assoc_ty_def(selcx, - impl_data.impl_def_id, - obligation.predicate.item_name) { - if node_item.node.is_from_trait() { - if node_item.item.ty.is_some() { - // If the associated type has a default from the - // trait, that should be considered `default` and - // hence not projected. - // - // Note, however, that we allow a projection from - // the trait specifically in the case that the trait - // does *not* give a default. This is purely to - // avoid spurious errors: the situation can only - // arise when *no* impl in the specialization chain - // has provided a definition for the type. When we - // confirm the candidate, we'll turn the projection - // into a TyError, since the actual error will be - // reported in `check_impl_items_against_trait`. - return None; - } - } else if node_item.item.defaultness.is_default() { - return None; - } - } else { - // Normally this situation could only arise througha - // compiler bug, but at coherence-checking time we only look - // at the topmost impl (we don't even consider the trait - // itself) for the definition -- so we can fail to find a - // definition of the type even if it exists. - - // For now, we just unconditionally ICE, because otherwise, - // examples like the following will succeed: - // - // ``` - // trait Assoc { - // type Output; - // } - // - // impl Assoc for T { - // default type Output = bool; - // } - // - // impl Assoc for u8 {} - // impl Assoc for u16 {} - // - // trait Foo {} - // impl Foo for ::Output {} - // impl Foo for ::Output {} - // return None; - // } - // ``` - // - // The essential problem here is that the projection fails, - // leaving two unnormalized types, which appear not to unify - // -- so the overlap check succeeds, when it should fail. - bug!("Tried to project an inherited associated type during \ - coherence checking, which is currently not supported."); - } - } - } - Some(candidate) - }); - - match possible_candidate { + match candidates.vec.pop() { Some(candidate) => { - let (ty, obligations) = confirm_candidate(selcx, obligation, candidate); + let (ty, obligations) = confirm_candidate(selcx, + obligation, + &obligation_trait_ref, + candidate); Ok(ProjectedTy::Progress(ty, obligations)) } None => { @@ -802,38 +728,6 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>( } } -fn assemble_candidates_from_object_type<'cx, 'gcx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, - candidate_set: &mut ProjectionTyCandidateSet<'tcx>) -{ - let self_ty = obligation_trait_ref.self_ty(); - let object_ty = selcx.infcx().shallow_resolve(self_ty); - debug!("assemble_candidates_from_object_type(object_ty={:?})", - object_ty); - let data = match object_ty.sty { - ty::TyTrait(ref data) => data, - _ => { - span_bug!( - obligation.cause.span, - "assemble_candidates_from_object_type called with non-object: {:?}", - object_ty); - } - }; - let projection_bounds = data.projection_bounds_with_self_ty(selcx.tcx(), object_ty); - let env_predicates = projection_bounds.iter() - .map(|p| p.to_predicate()) - .collect(); - let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates); - assemble_candidates_from_predicates(selcx, - obligation, - obligation_trait_ref, - candidate_set, - ProjectionTyCandidate::ParamEnv, - env_predicates) -} - fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>( selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -845,82 +739,183 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>( // start out by selecting the predicate `T as TraitRef<...>`: let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate()); - let vtable = match selcx.select(&trait_obligation) { - Ok(Some(vtable)) => vtable, - Ok(None) => { - candidate_set.ambiguous = true; - return Ok(()); - } - Err(e) => { - debug!("assemble_candidates_from_impls: selection error {:?}", - e); - return Err(e); - } - }; + selcx.infcx().probe(|_| { + let vtable = match selcx.select(&trait_obligation) { + Ok(Some(vtable)) => vtable, + Ok(None) => { + candidate_set.ambiguous = true; + return Ok(()); + } + Err(e) => { + debug!("assemble_candidates_from_impls: selection error {:?}", + e); + return Err(e); + } + }; - match vtable { - super::VtableImpl(data) => { - debug!("assemble_candidates_from_impls: impl candidate {:?}", - data); + match vtable { + super::VtableClosure(_) | + super::VtableFnPointer(_) | + super::VtableObject(_) => { + debug!("assemble_candidates_from_impls: vtable={:?}", + vtable); - candidate_set.vec.push( - ProjectionTyCandidate::Impl(data)); - } - super::VtableObject(_) => { - assemble_candidates_from_object_type( - selcx, obligation, obligation_trait_ref, candidate_set); - } - super::VtableClosure(data) => { - candidate_set.vec.push( - ProjectionTyCandidate::Closure(data)); - } - super::VtableFnPointer(data) => { - candidate_set.vec.push( - ProjectionTyCandidate::FnPointer(data)); - } - super::VtableParam(..) => { - // This case tell us nothing about the value of an - // associated type. Consider: - // - // ``` - // trait SomeTrait { type Foo; } - // fn foo(...) { } - // ``` - // - // If the user writes `::Foo`, then the `T - // : SomeTrait` binding does not help us decide what the - // type `Foo` is (at least, not more specifically than - // what we already knew). - // - // But wait, you say! What about an example like this: - // - // ``` - // fn bar>(...) { ... } - // ``` - // - // Doesn't the `T : Sometrait` predicate help - // resolve `T::Foo`? And of course it does, but in fact - // that single predicate is desugared into two predicates - // in the compiler: a trait predicate (`T : SomeTrait`) and a - // projection. And the projection where clause is handled - // in `assemble_candidates_from_param_env`. - } - super::VtableDefaultImpl(..) | - super::VtableBuiltin(..) => { - // These traits have no associated types. - span_bug!( - obligation.cause.span, - "Cannot project an associated type from `{:?}`", - vtable); + candidate_set.vec.push(ProjectionTyCandidate::Select); + } + super::VtableImpl(ref impl_data) if !selcx.projection_mode().is_any() => { + // We have to be careful when projecting out of an + // impl because of specialization. If we are not in + // trans (i.e., projection mode is not "any"), and the + // impl's type is declared as default, then we disable + // projection (even if the trait ref is fully + // monomorphic). In the case where trait ref is not + // fully monomorphic (i.e., includes type parameters), + // this is because those type parameters may + // ultimately be bound to types from other crates that + // may have specialized impls we can't see. In the + // case where the trait ref IS fully monomorphic, this + // is a policy decision that we made in the RFC in + // order to preserve flexibility for the crate that + // defined the specializable impl to specialize later + // for existing types. + // + // In either case, we handle this by not adding a + // candidate for an impl if it contains a `default` + // type. + let opt_node_item = assoc_ty_def(selcx, + impl_data.impl_def_id, + obligation.predicate.item_name); + let new_candidate = if let Some(node_item) = opt_node_item { + if node_item.node.is_from_trait() { + if node_item.item.ty.is_some() { + // The impl inherited a `type Foo = + // Bar` given in the trait, which is + // implicitly default. No candidate. + None + } else { + // The impl did not specify `type` and neither + // did the trait: + // + // ```rust + // trait Foo { type T; } + // impl Foo for Bar { } + // ``` + // + // This is an error, but it will be + // reported in `check_impl_items_against_trait`. + // We accept it here but will flag it as + // an error when we confirm the candidate + // (which will ultimately lead to `normalize_to_error` + // being invoked). + Some(ProjectionTyCandidate::Select) + } + } else if node_item.item.defaultness.is_default() { + // The impl specified `default type Foo = + // Bar`. No candidate. + None + } else { + // The impl specified `type Foo = Bar` + // with no default. Add a candidate. + Some(ProjectionTyCandidate::Select) + } + } else { + // This is saying that neither the trait nor + // the impl contain a definition for this + // associated type. Normally this situation + // could only arise through a compiler bug -- + // if the user wrote a bad item name, it + // should have failed in astconv. **However**, + // at coherence-checking time, we only look at + // the topmost impl (we don't even consider + // the trait itself) for the definition -- and + // so in that case it may be that the trait + // *DOES* have a declaration, but we don't see + // it, and we end up in this branch. + // + // This is kind of tricky to handle actually. + // For now, we just unconditionally ICE, + // because otherwise, examples like the + // following will succeed: + // + // ``` + // trait Assoc { + // type Output; + // } + // + // impl Assoc for T { + // default type Output = bool; + // } + // + // impl Assoc for u8 {} + // impl Assoc for u16 {} + // + // trait Foo {} + // impl Foo for ::Output {} + // impl Foo for ::Output {} + // return None; + // } + // ``` + // + // The essential problem here is that the + // projection fails, leaving two unnormalized + // types, which appear not to unify -- so the + // overlap check succeeds, when it should + // fail. + bug!("Tried to project an inherited associated type during \ + coherence checking, which is currently not supported."); + }; + candidate_set.vec.extend(new_candidate); + } + super::VtableImpl(_) => { + // In trans mode, we can just project out of impls, no prob. + assert!(selcx.projection_mode().is_any()); + candidate_set.vec.push(ProjectionTyCandidate::Select); + } + super::VtableParam(..) => { + // This case tell us nothing about the value of an + // associated type. Consider: + // + // ``` + // trait SomeTrait { type Foo; } + // fn foo(...) { } + // ``` + // + // If the user writes `::Foo`, then the `T + // : SomeTrait` binding does not help us decide what the + // type `Foo` is (at least, not more specifically than + // what we already knew). + // + // But wait, you say! What about an example like this: + // + // ``` + // fn bar>(...) { ... } + // ``` + // + // Doesn't the `T : Sometrait` predicate help + // resolve `T::Foo`? And of course it does, but in fact + // that single predicate is desugared into two predicates + // in the compiler: a trait predicate (`T : SomeTrait`) and a + // projection. And the projection where clause is handled + // in `assemble_candidates_from_param_env`. + } + super::VtableDefaultImpl(..) | + super::VtableBuiltin(..) => { + // These traits have no associated types. + span_bug!( + obligation.cause.span, + "Cannot project an associated type from `{:?}`", + vtable); + } } - } - Ok(()) + Ok(()) + }) } fn confirm_candidate<'cx, 'gcx, 'tcx>( selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, candidate: ProjectionTyCandidate<'tcx>) -> (Ty<'tcx>, Vec>) { @@ -934,18 +929,116 @@ fn confirm_candidate<'cx, 'gcx, 'tcx>( confirm_param_env_candidate(selcx, obligation, poly_projection) } - ProjectionTyCandidate::Impl(impl_vtable) => { - confirm_impl_candidate(selcx, obligation, impl_vtable) + ProjectionTyCandidate::Select => { + confirm_select_candidate(selcx, obligation, obligation_trait_ref) + } + } +} + +fn confirm_select_candidate<'cx, 'gcx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>) + -> (Ty<'tcx>, Vec>) +{ + let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate()); + let vtable = match selcx.select(&trait_obligation) { + Ok(Some(vtable)) => vtable, + _ => { + span_bug!( + obligation.cause.span, + "Failed to select `{:?}`", + trait_obligation); } + }; - ProjectionTyCandidate::Closure(closure_vtable) => { - confirm_closure_candidate(selcx, obligation, closure_vtable) + match vtable { + super::VtableImpl(data) => + confirm_impl_candidate(selcx, obligation, data), + super::VtableClosure(data) => + confirm_closure_candidate(selcx, obligation, data), + super::VtableFnPointer(data) => + confirm_fn_pointer_candidate(selcx, obligation, data), + super::VtableObject(_) => + confirm_object_candidate(selcx, obligation, obligation_trait_ref), + super::VtableDefaultImpl(..) | + super::VtableParam(..) | + super::VtableBuiltin(..) => + // we don't create Select candidates with this kind of resolution + span_bug!( + obligation.cause.span, + "Cannot project an associated type from `{:?}`", + vtable), + } +} + +fn confirm_object_candidate<'cx, 'gcx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>) + -> (Ty<'tcx>, Vec>) +{ + let self_ty = obligation_trait_ref.self_ty(); + let object_ty = selcx.infcx().shallow_resolve(self_ty); + debug!("assemble_candidates_from_object_type(object_ty={:?})", + object_ty); + let data = match object_ty.sty { + ty::TyTrait(ref data) => data, + _ => { + span_bug!( + obligation.cause.span, + "assemble_candidates_from_object_type called with non-object: {:?}", + object_ty); } + }; + let projection_bounds = data.projection_bounds_with_self_ty(selcx.tcx(), object_ty); + let env_predicates = projection_bounds.iter() + .map(|p| p.to_predicate()) + .collect(); + let env_predicate = { + let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates); + + // select only those projections that are actually projecting an + // item with the correct name + let env_predicates = env_predicates.filter_map(|p| match p { + ty::Predicate::Projection(data) => + if data.item_name() == obligation.predicate.item_name { + Some(data) + } else { + None + }, + _ => None + }); - ProjectionTyCandidate::FnPointer(fn_pointer_vtable) => { - confirm_fn_pointer_candidate(selcx, obligation, fn_pointer_vtable) + // select those with a relevant trait-ref + let mut env_predicates = env_predicates.filter(|data| { + let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span); + let data_poly_trait_ref = data.to_poly_trait_ref(); + let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + selcx.infcx().probe(|_| { + selcx.infcx().sub_poly_trait_refs(false, + origin, + data_poly_trait_ref, + obligation_poly_trait_ref).is_ok() + }) + }); + + // select the first matching one; there really ought to be one or + // else the object type is not WF, since an object type should + // include all of its projections explicitly + match env_predicates.next() { + Some(env_predicate) => env_predicate, + None => { + debug!("confirm_object_candidate: no env-predicate \ + found in object type `{:?}`; ill-formed", + object_ty); + return (selcx.tcx().types.err, vec!()); + } } - } + }; + + confirm_param_env_candidate(selcx, obligation, env_predicate) } fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>( From 97ca8d799c2b6e6a4ce1b55b7d6e28526d3f23d5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 21 Apr 2016 05:14:58 -0400 Subject: [PATCH 2/4] fix -Z treat-err-as-bug --- src/libsyntax/errors/mod.rs | 60 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs index 7592214c0ab92..2ca61ba76d47c 100644 --- a/src/libsyntax/errors/mod.rs +++ b/src/libsyntax/errors/mod.rs @@ -180,7 +180,7 @@ impl error::Error for ExplicitBug { #[must_use] #[derive(Clone)] pub struct DiagnosticBuilder<'a> { - emitter: &'a RefCell>, + handler: &'a Handler, level: Level, message: String, code: Option, @@ -204,8 +204,9 @@ impl<'a> DiagnosticBuilder<'a> { return; } - self.emitter.borrow_mut().emit_struct(&self); + self.handler.emit.borrow_mut().emit_struct(&self); self.cancel(); + self.handler.panic_if_treat_err_as_bug(); // if self.is_fatal() { // panic!(FatalError); @@ -321,11 +322,11 @@ impl<'a> DiagnosticBuilder<'a> { /// Convenience function for internal use, clients should use one of the /// struct_* methods on Handler. - fn new(emitter: &'a RefCell>, + fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { DiagnosticBuilder { - emitter: emitter, + handler: handler, level: level, message: message.to_owned(), code: None, @@ -362,10 +363,10 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> { impl<'a> Drop for DiagnosticBuilder<'a> { fn drop(&mut self) { if !panicking() && !self.cancelled() { - self.emitter.borrow_mut().emit(&MultiSpan::new(), - "Error constructed but not emitted", - None, - Bug); + self.handler.emit.borrow_mut().emit(&MultiSpan::new(), + "Error constructed but not emitted", + None, + Bug); panic!(); } } @@ -412,14 +413,14 @@ impl Handler { } pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> { - DiagnosticBuilder::new(&self.emit, Level::Cancelled, "") + DiagnosticBuilder::new(self, Level::Cancelled, "") } pub fn struct_span_warn<'a, S: Into>(&'a self, sp: S, msg: &str) -> DiagnosticBuilder<'a> { - let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); + let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); result.set_span(sp); if !self.can_emit_warnings { result.cancel(); @@ -431,7 +432,7 @@ impl Handler { msg: &str, code: &str) -> DiagnosticBuilder<'a> { - let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); + let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); result.set_span(sp); result.code(code.to_owned()); if !self.can_emit_warnings { @@ -440,7 +441,7 @@ impl Handler { result } pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { - let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); + let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); if !self.can_emit_warnings { result.cancel(); } @@ -451,7 +452,7 @@ impl Handler { msg: &str) -> DiagnosticBuilder<'a> { self.bump_err_count(); - let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg); + let mut result = DiagnosticBuilder::new(self, Level::Error, msg); result.set_span(sp); result } @@ -461,21 +462,21 @@ impl Handler { code: &str) -> DiagnosticBuilder<'a> { self.bump_err_count(); - let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg); + let mut result = DiagnosticBuilder::new(self, Level::Error, msg); result.set_span(sp); result.code(code.to_owned()); result } pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { self.bump_err_count(); - DiagnosticBuilder::new(&self.emit, Level::Error, msg) + DiagnosticBuilder::new(self, Level::Error, msg) } pub fn struct_span_fatal<'a, S: Into>(&'a self, sp: S, msg: &str) -> DiagnosticBuilder<'a> { self.bump_err_count(); - let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg); + let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); result.set_span(sp); result } @@ -485,14 +486,14 @@ impl Handler { code: &str) -> DiagnosticBuilder<'a> { self.bump_err_count(); - let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg); + let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); result.set_span(sp); result.code(code.to_owned()); result } pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { self.bump_err_count(); - DiagnosticBuilder::new(&self.emit, Level::Fatal, msg) + DiagnosticBuilder::new(self, Level::Fatal, msg) } pub fn cancel(&mut self, err: &mut DiagnosticBuilder) { @@ -503,36 +504,35 @@ impl Handler { err.cancel(); } - pub fn span_fatal>(&self, sp: S, msg: &str) -> FatalError { + fn panic_if_treat_err_as_bug(&self) { if self.treat_err_as_bug { - self.span_bug(sp, msg); + panic!("encountered error with `-Z treat_err_as_bug"); } + } + + pub fn span_fatal>(&self, sp: S, msg: &str) + -> FatalError { self.emit(&sp.into(), msg, Fatal); self.bump_err_count(); + self.panic_if_treat_err_as_bug(); return FatalError; } pub fn span_fatal_with_code>(&self, sp: S, msg: &str, code: &str) - -> FatalError { - if self.treat_err_as_bug { - self.span_bug(sp, msg); - } + -> FatalError { self.emit_with_code(&sp.into(), msg, code, Fatal); self.bump_err_count(); + self.panic_if_treat_err_as_bug(); return FatalError; } pub fn span_err>(&self, sp: S, msg: &str) { - if self.treat_err_as_bug { - self.span_bug(sp, msg); - } self.emit(&sp.into(), msg, Error); self.bump_err_count(); + self.panic_if_treat_err_as_bug(); } pub fn span_err_with_code>(&self, sp: S, msg: &str, code: &str) { - if self.treat_err_as_bug { - self.span_bug(sp, msg); - } self.emit_with_code(&sp.into(), msg, code, Error); self.bump_err_count(); + self.panic_if_treat_err_as_bug(); } pub fn span_warn>(&self, sp: S, msg: &str) { self.emit(&sp.into(), msg, Warning); From ccfb74e800eff83f316f55ecae337569d7e83ee4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Mar 2016 06:37:12 -0400 Subject: [PATCH 3/4] warn for where/return-types that reference regions This is a step towards fixing #32330. The full fix would be a breaking change, so we begin by issuing warnings for scenarios that will break. --- src/librustc/lint/builtin.rs | 10 +- src/librustc/ty/fold.rs | 80 ++++++++++++++++ src/librustc_lint/lib.rs | 6 +- src/librustc_typeck/astconv.rs | 95 +++++++++++++++++-- src/librustc_typeck/check/mod.rs | 1 + src/librustc_typeck/collect.rs | 3 +- .../compile-fail/associated-types-eq-hr.rs | 49 +++++++++- .../bound-lifetime-constrained.rs | 66 +++++++++++++ .../bound-lifetime-in-binding-only.rs | 90 ++++++++++++++++++ .../bound-lifetime-in-return-only.rs | 64 +++++++++++++ 10 files changed, 449 insertions(+), 15 deletions(-) create mode 100644 src/test/compile-fail/associated-types/bound-lifetime-constrained.rs create mode 100644 src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs create mode 100644 src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 6dd98425df317..d7971cd2cf040 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -167,6 +167,13 @@ declare_lint! { "transmute from function item type to pointer-sized type erroneously allowed" } +declare_lint! { + pub HR_LIFETIME_IN_ASSOC_TYPE, + Warn, + "binding for associated type references higher-ranked lifetime \ + that does not appear in the trait input types" +} + declare_lint! { pub OVERLAPPING_INHERENT_IMPLS, Warn, @@ -234,7 +241,8 @@ impl LintPass for HardwiredLints { RENAMED_AND_REMOVED_LINTS, SUPER_OR_SELF_IN_GLOBAL_PATH, UNSIZED_IN_TUPLE, - OBJECT_UNSAFE_FRAGMENT + OBJECT_UNSAFE_FRAGMENT, + HR_LIFETIME_IN_ASSOC_TYPE ) } } diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 14b369f244d49..4a14185b6e3ad 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -382,6 +382,35 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + /// Returns a set of all late-bound regions that are constrained + /// by `value`, meaning that if we instantiate those LBR with + /// variables and equate `value` with something else, those + /// variables will also be equated. + pub fn collect_constrained_late_bound_regions(&self, value: &Binder) + -> FnvHashSet + where T : TypeFoldable<'tcx> + { + self.collect_late_bound_regions(value, true) + } + + /// Returns a set of all late-bound regions that appear in `value` anywhere. + pub fn collect_referenced_late_bound_regions(&self, value: &Binder) + -> FnvHashSet + where T : TypeFoldable<'tcx> + { + self.collect_late_bound_regions(value, false) + } + + fn collect_late_bound_regions(&self, value: &Binder, just_constraint: bool) + -> FnvHashSet + where T : TypeFoldable<'tcx> + { + let mut collector = LateBoundRegionsCollector::new(just_constraint); + let result = value.skip_binder().visit_with(&mut collector); + assert!(!result); // should never have stopped early + collector.regions + } + /// Replace any late-bound regions bound in `value` with `'static`. Useful in trans but also /// method lookup and a few other places where precise region relationships are not required. pub fn erase_late_bound_regions(self, value: &Binder) -> T @@ -625,3 +654,54 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { false } } + +/// Collects all the late-bound regions it finds into a hash set. +struct LateBoundRegionsCollector { + current_depth: u32, + regions: FnvHashSet, + just_constrained: bool, +} + +impl LateBoundRegionsCollector { + fn new(just_constrained: bool) -> Self { + LateBoundRegionsCollector { + current_depth: 1, + regions: FnvHashSet(), + just_constrained: just_constrained, + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { + fn visit_binder>(&mut self, t: &Binder) -> bool { + self.current_depth += 1; + let result = t.super_visit_with(self); + self.current_depth -= 1; + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + // if we are only looking for "constrained" region, we have to + // ignore the inputs to a projection, as they may not appear + // in the normalized form + if self.just_constrained { + match t.sty { + ty::TyProjection(..) => { return false; } + _ => { } + } + } + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region) -> bool { + match r { + ty::ReLateBound(debruijn, br) if debruijn.depth == self.current_depth => { + self.regions.insert(br); + } + _ => { } + } + false + } +} + diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index e0abe1aebd28d..9fca6d3d20139 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -197,7 +197,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { FutureIncompatibleInfo { id: LintId::of(OBJECT_UNSAFE_FRAGMENT), reference: "issue #33243 ", - } + }, + FutureIncompatibleInfo { + id: LintId::of(HR_LIFETIME_IN_ASSOC_TYPE), + reference: "issue #33685 ", + }, ]); // We have one lint pass defined specially diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 4faefb610562d..ac86b7c874060 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -52,13 +52,17 @@ use middle::const_val::ConstVal; use rustc_const_eval::{eval_const_expr_partial, ConstEvalErr}; use rustc_const_eval::EvalHint::UncheckedExprHint; use rustc_const_eval::ErrKind::ErroneousReferencedConstant; +use hir::{self, SelfKind}; use hir::def::{self, Def}; use hir::def_id::DefId; +use hir::print as pprust; use middle::resolve_lifetime as rl; +use rustc::lint; use rustc::ty::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace}; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable}; use rustc::ty::wf::object_region_bounds; +use rustc_back::slice; use require_c_abi_if_variadic; use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope, ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope, @@ -74,10 +78,6 @@ use syntax::errors::DiagnosticBuilder; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::parse::token::{self, keywords}; -use rustc::hir::print as pprust; -use rustc::hir::{self, SelfKind}; -use rustc_back::slice; - pub trait AstConv<'gcx, 'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>; @@ -679,6 +679,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { PathParamMode::Explicit, trait_def_id, self_ty, + trait_ref.ref_id, trait_ref.path.segments.last().unwrap(), poly_projections) } @@ -723,6 +724,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { span: Span, param_mode: PathParamMode, trait_def_id: DefId, + trait_path_ref_id: ast::NodeId, trait_segment: &hir::PathSegment, mut projections: &mut Vec>) -> ty::PolyTraitRef<'tcx> @@ -732,6 +734,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode, trait_def_id, None, + trait_path_ref_id, trait_segment, projections) } @@ -742,6 +745,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode: PathParamMode, trait_def_id: DefId, self_ty: Option>, + path_id: ast::NodeId, trait_segment: &hir::PathSegment, poly_projections: &mut Vec>) -> ty::PolyTraitRef<'tcx> @@ -770,7 +774,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { .filter_map(|binding| { // specify type to assert that error was already reported in Err case: let predicate: Result<_, ErrorReported> = - self.ast_type_binding_to_poly_projection_predicate(poly_trait_ref.clone(), + self.ast_type_binding_to_poly_projection_predicate(path_id, + poly_trait_ref.clone(), self_ty, binding); predicate.ok() // ok to ignore Err() because ErrorReported (see above) @@ -863,7 +868,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { (self.tcx().mk_substs(substs), assoc_bindings) } - fn ast_type_binding_to_poly_projection_predicate(&self, + fn ast_type_binding_to_poly_projection_predicate( + &self, + path_id: ast::NodeId, mut trait_ref: ty::PolyTraitRef<'tcx>, self_ty: Option>, binding: &ConvertedBinding<'tcx>) @@ -887,6 +894,36 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { // // We want to produce `>::T == foo`. + // Find any late-bound regions declared in `ty` that are not + // declared in the trait-ref. These are not wellformed. + // + // Example: + // + // for<'a> ::Item = &'a str // <-- 'a is bad + // for<'a> >::Output = &'a str // <-- 'a is ok + let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&trait_ref); + let late_bound_in_ty = tcx.collect_referenced_late_bound_regions(&ty::Binder(binding.ty)); + debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref); + debug!("late_bound_in_ty = {:?}", late_bound_in_ty); + for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) { + let br_name = match *br { + ty::BrNamed(_, name) => name, + _ => { + span_bug!( + binding.span, + "anonymous bound region {:?} in binding but not trait ref", + br); + } + }; + tcx.sess.add_lint( + lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE, + path_id, + binding.span, + format!("binding for associated type `{}` references lifetime `{}`, \ + which does not appear in the trait input types", + binding.item_name, br_name)); + } + // Simple case: X is defined in the current trait. if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) { return Ok(ty::Binder(ty::ProjectionPredicate { // <-------------------+ @@ -1012,6 +1049,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { path.span, PathParamMode::Explicit, trait_def_id, + ty.id, path.segments.last().unwrap(), &mut projection_bounds); Ok((trait_ref, projection_bounds)) @@ -1416,6 +1454,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode: PathParamMode, def: Def, opt_self_ty: Option>, + base_path_ref_id: ast::NodeId, base_segments: &[hir::PathSegment]) -> Ty<'tcx> { let tcx = self.tcx(); @@ -1434,6 +1473,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { span, param_mode, trait_def_id, + base_path_ref_id, base_segments.last().unwrap(), &mut projection_bounds); @@ -1518,6 +1558,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode: PathParamMode, mut def: Def, opt_self_ty: Option>, + base_path_ref_id: ast::NodeId, base_segments: &[hir::PathSegment], assoc_segments: &[hir::PathSegment]) -> (Ty<'tcx>, Def) { @@ -1532,6 +1573,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { param_mode, def, opt_self_ty, + base_path_ref_id, base_segments); debug!("finish_resolving_def_to_ty: base_def_to_ty returned {:?}", ty); // If any associated type segments remain, attempt to resolve them. @@ -1607,7 +1649,45 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } hir::TyBareFn(ref bf) => { require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); - tcx.mk_fn_ptr(self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl)) + let bare_fn_ty = self.ty_of_bare_fn(bf.unsafety, bf.abi, &bf.decl); + + // Find any late-bound regions declared in return type that do + // not appear in the arguments. These are not wellformed. + // + // Example: + // + // for<'a> fn() -> &'a str <-- 'a is bad + // for<'a> fn(&'a String) -> &'a str <-- 'a is ok + // + // Note that we do this check **here** and not in + // `ty_of_bare_fn` because the latter is also used to make + // the types for fn items, and we do not want to issue a + // warning then. (Once we fix #32330, the regions we are + // checking for here would be considered early bound + // anyway.) + let inputs = bare_fn_ty.sig.inputs(); + let late_bound_in_args = tcx.collect_constrained_late_bound_regions(&inputs); + let output = bare_fn_ty.sig.output(); + let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output); + for br in late_bound_in_ret.difference(&late_bound_in_args) { + let br_name = match *br { + ty::BrNamed(_, name) => name, + _ => { + span_bug!( + bf.decl.output.span(), + "anonymous bound region {:?} in return but not args", + br); + } + }; + tcx.sess.add_lint( + lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE, + ast_ty.id, + ast_ty.span, + format!("return type references lifetime `{}`, \ + which does not appear in the trait input types", + br_name)); + } + tcx.mk_fn_ptr(bare_fn_ty) } hir::TyPolyTraitRef(ref bounds) => { self.conv_ty_poly_trait_ref(rscope, ast_ty.span, bounds) @@ -1635,6 +1715,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { PathParamMode::Explicit, def, opt_self_ty, + ast_ty.id, &path.segments[..base_ty_end], &path.segments[base_ty_end..]); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ed2edc30c9d31..1ee9d1032a6ee 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3866,6 +3866,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { PathParamMode::Optional, def, opt_self_ty, + node_id, &ty_segments[..base_ty_end], &ty_segments[base_ty_end..]); let item_segment = path.segments.last().unwrap(); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index f9a22e2a577b4..5896a34b0d160 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -568,7 +568,8 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let (fty, explicit_self_category) = AstConv::ty_of_method(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)), - sig, untransformed_rcvr_ty); + sig, + untransformed_rcvr_ty); let def_id = ccx.tcx.map.local_def_id(id); let substs = mk_item_substs(ccx, &ty_generics); diff --git a/src/test/compile-fail/associated-types-eq-hr.rs b/src/test/compile-fail/associated-types-eq-hr.rs index d5678c155fd2d..52a2ca9082d23 100644 --- a/src/test/compile-fail/associated-types-eq-hr.rs +++ b/src/test/compile-fail/associated-types-eq-hr.rs @@ -40,6 +40,17 @@ impl<'a> TheTrait<&'a isize> for UintStruct { } } +struct Tuple { +} + +impl<'a> TheTrait<(&'a isize, &'a isize)> for Tuple { + type A = &'a isize; + + fn get(&self, t: (&'a isize, &'a isize)) -> &'a isize { + t.0 + } +} + fn foo() where T : for<'x> TheTrait<&'x isize, A = &'x isize> { @@ -52,10 +63,28 @@ fn bar() // ok for UintStruct, but not IntStruct } -fn baz() - where T : for<'x,'y> TheTrait<&'x isize, A = &'y isize> +fn tuple_one() + where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize> +{ + // not ok for tuple, two lifetimes and we pick first +} + +fn tuple_two() + where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize> { - // not ok for either struct, due to the use of two lifetimes + // not ok for tuple, two lifetimes and we pick second +} + +fn tuple_three() + where T : for<'x> TheTrait<(&'x isize, &'x isize), A = &'x isize> +{ + // ok for tuple +} + +fn tuple_four() + where T : for<'x,'y> TheTrait<(&'x isize, &'y isize)> +{ + // not ok for tuple, two lifetimes, and lifetime matching is invariant } pub fn main() { @@ -65,6 +94,16 @@ pub fn main() { bar::(); //~ ERROR type mismatch bar::(); - baz::(); //~ ERROR type mismatch - baz::(); //~ ERROR type mismatch + tuple_one::(); + //~^ ERROR E0277 + //~| ERROR type mismatch + + tuple_two::(); + //~^ ERROR E0277 + //~| ERROR type mismatch + + tuple_three::(); + + tuple_four::(); + //~^ ERROR E0277 } diff --git a/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs b/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs new file mode 100644 index 0000000000000..f60f06b4ec833 --- /dev/null +++ b/src/test/compile-fail/associated-types/bound-lifetime-constrained.rs @@ -0,0 +1,66 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: func object clause + +#![allow(dead_code)] +#![feature(rustc_attrs)] +#![feature(unboxed_closures)] +#![deny(hr_lifetime_in_assoc_type)] + +trait Foo<'a> { + type Item; +} + +impl<'a> Foo<'a> for() { + type Item = (); +} + +// Check that appearing in a projection input in the argument is not enough: +#[cfg(func)] +fn func1(_: for<'a> fn(<() as Foo<'a>>::Item) -> &'a i32) { + //[func]~^ ERROR return type references lifetime `'a` + //[func]~| WARNING previously accepted +} + +// Check that appearing in a projection input in the return still +// causes an error: +#[cfg(func)] +fn func2(_: for<'a> fn() -> <() as Foo<'a>>::Item) { + //[func]~^ ERROR return type references lifetime `'a` + //[func]~| WARNING previously accepted +} + +#[cfg(object)] +fn object1(_: Box Fn(<() as Foo<'a>>::Item) -> &'a i32>) { + //[object]~^ ERROR `Output` references lifetime `'a` + //[object]~| WARNING previously accepted +} + +#[cfg(object)] +fn object2(_: Box Fn() -> <() as Foo<'a>>::Item>) { + //[object]~^ ERROR `Output` references lifetime `'a` + //[object]~| WARNING previously accepted +} + +#[cfg(clause)] +fn clause1() where T: for<'a> Fn(<() as Foo<'a>>::Item) -> &'a i32 { + //[clause]~^ ERROR `Output` references lifetime `'a` + //[clause]~| WARNING previously accepted +} + +#[cfg(clause)] +fn clause2() where T: for<'a> Fn() -> <() as Foo<'a>>::Item { + //[clause]~^ ERROR `Output` references lifetime `'a` + //[clause]~| WARNING previously accepted +} + +#[rustc_error] +fn main() { } //[ok]~ ERROR compilation successful diff --git a/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs b/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs new file mode 100644 index 0000000000000..020c9e5e1db59 --- /dev/null +++ b/src/test/compile-fail/associated-types/bound-lifetime-in-binding-only.rs @@ -0,0 +1,90 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: angle paren ok elision + +#![allow(dead_code)] +#![feature(rustc_attrs)] +#![feature(unboxed_closures)] +#![deny(hr_lifetime_in_assoc_type)] + +trait Foo { + type Item; +} + +#[cfg(angle)] +fn angle Foo>() { + //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` + //[angle]~| WARNING previously accepted +} + +#[cfg(angle)] +fn angle1() where T: for<'a> Foo { + //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` + //[angle]~| WARNING previously accepted +} + +#[cfg(angle)] +fn angle2() where for<'a> T: Foo { + //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` + //[angle]~| WARNING previously accepted +} + +#[cfg(angle)] +fn angle3(_: &for<'a> Foo) { + //[angle]~^ ERROR binding for associated type `Item` references lifetime `'a` + //[angle]~| WARNING previously accepted +} + +#[cfg(paren)] +fn paren Fn() -> &'a i32>() { + //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a` + //[paren]~| WARNING previously accepted +} + +#[cfg(paren)] +fn paren1() where T: for<'a> Fn() -> &'a i32 { + //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a` + //[paren]~| WARNING previously accepted +} + +#[cfg(paren)] +fn paren2() where for<'a> T: Fn() -> &'a i32 { + //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a` + //[paren]~| WARNING previously accepted +} + +#[cfg(paren)] +fn paren3(_: &for<'a> Fn() -> &'a i32) { + //[paren]~^ ERROR binding for associated type `Output` references lifetime `'a` + //[paren]~| WARNING previously accepted +} + +#[cfg(elision)] +fn elision &i32>() { + //[elision]~^ ERROR E0106 +} + +struct Parameterized<'a> { x: &'a str } + +#[cfg(ok)] +fn ok1 Fn(&Parameterized<'a>) -> &'a i32>() { +} + +#[cfg(ok)] +fn ok2 Fn<(&'b Parameterized<'a>,), Output=&'a i32>>() { +} + +#[cfg(ok)] +fn ok3() where for<'a> Parameterized<'a>: Foo { +} + +#[rustc_error] +fn main() { } //[ok]~ ERROR compilation successful diff --git a/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs b/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs new file mode 100644 index 0000000000000..0b4a9bf58a66c --- /dev/null +++ b/src/test/compile-fail/associated-types/bound-lifetime-in-return-only.rs @@ -0,0 +1,64 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: sig local structure ok elision + +#![allow(dead_code)] +#![feature(rustc_attrs)] +#![feature(unboxed_closures)] +#![deny(hr_lifetime_in_assoc_type)] + +trait Foo { + type Item; +} + +#[cfg(sig)] +fn sig1(_: for<'a> fn() -> &'a i32) { + //[sig]~^ ERROR return type references lifetime `'a` + //[sig]~| WARNING previously accepted +} + +#[cfg(sig)] +fn sig2(_: for<'a, 'b> fn(&'b i32) -> &'a i32) { + //[sig]~^ ERROR return type references lifetime `'a` + //[sig]~| WARNING previously accepted +} + +#[cfg(local)] +fn local1() { + let _: for<'a> fn() -> &'a i32 = loop { }; + //[local]~^ ERROR return type references lifetime `'a` + //[local]~| WARNING previously accepted +} + +#[cfg(structure)] +struct Struct1 { + x: for<'a> fn() -> &'a i32 + //[structure]~^ ERROR return type references lifetime `'a` + //[structure]~| WARNING previously accepted +} + +#[cfg(elision)] +fn elision(_: fn() -> &i32) { + //[elision]~^ ERROR E0106 +} + +struct Parameterized<'a> { x: &'a str } + +#[cfg(ok)] +fn ok1(_: &for<'a> Fn(&Parameterized<'a>) -> &'a i32) { +} + +#[cfg(ok)] +fn ok2(_: &for<'a,'b> Fn<(&'b Parameterized<'a>,), Output=&'a i32>) { +} + +#[rustc_error] +fn main() { } //[ok]~ ERROR compilation successful From 639890d92da1bbe62dc669ff1a122a23da7bc0cd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Apr 2016 15:28:52 -0400 Subject: [PATCH 4/4] fix stale method names --- src/librustc/traits/project.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index b6f7e19b04a56..5c7095beb79c8 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -981,14 +981,14 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>( { let self_ty = obligation_trait_ref.self_ty(); let object_ty = selcx.infcx().shallow_resolve(self_ty); - debug!("assemble_candidates_from_object_type(object_ty={:?})", + debug!("confirm_object_candidate(object_ty={:?})", object_ty); let data = match object_ty.sty { ty::TyTrait(ref data) => data, _ => { span_bug!( obligation.cause.span, - "assemble_candidates_from_object_type called with non-object: {:?}", + "confirm_object_candidate called with non-object: {:?}", object_ty); } };