diff options
| author | bors <bors@rust-lang.org> | 2016-11-04 07:20:44 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-11-04 07:20:44 -0700 |
| commit | ccfc38f034e3f53cb460936bd9744085d4a63b40 (patch) | |
| tree | 2f7c7808ef2f74a2c40d4c296808be5e2b8edf03 | |
| parent | d2bc30b03fa4bf5425d080710f681f36f58f1706 (diff) | |
| parent | 4501e5a52f6268a156e95a8fe72ae17c47bf6ee3 (diff) | |
| download | rust-ccfc38f034e3f53cb460936bd9744085d4a63b40.tar.gz rust-ccfc38f034e3f53cb460936bd9744085d4a63b40.zip | |
Auto merge of #37167 - nikomatsakis:jroesch-issue-18937, r=pnkfelix
detect extra region requirements in impls
The current "compare method" check fails to check for the "region obligations" that accrue in the fulfillment context. This branch switches that code to create a `FnCtxt` so that it can invoke the regionck code. Previous crater runs (I haven't done one with the latest tip) have found some small number of affected crates, so I went ahead and introduced a warning cycle. I will kick off a crater run with this branch shortly.
This is a [breaking-change] because previously unsound code was accepted. The crater runs also revealed some cases where legitimate code was no longer type-checking, so the branch contains one additional (but orthogonal) change. It improves the elaborator so that we elaborate region requirements more thoroughly. In particular, if we know that `&'a T: 'b`, we now deduce that `T: 'b` and `'a: 'b`.
I invested a certain amount of effort in getting a good error message. The error message looks like this:
```
error[E0276]: impl has stricter requirements than trait
--> traits-elaborate-projection-region.rs:33:5
|
21 | fn foo() where T: 'a;
| --------------------- definition of `foo` from trait
...
33 | fn foo() where U: 'a { }
| ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `U: 'a`
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #18937 <https://github.com/rust-lang/rust/issues/18937>
note: lint level defined here
--> traits-elaborate-projection-region.rs:12:9
|
12 | #![deny(extra_requirement_in_impl)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
```
Obviously the warning only prints if this is a _new_ error (that resulted from the bugfix). But all existing errors that fit this description are updated to follow the general template. In order to get the lint to preserve the span-labels and the error code, I separate out the core `Diagnostic` type (which encapsulates the error code, message, span, and children) from the `DiagnosticBuilder` (which layers on a `Handler` that can be used to report errors). I also extended `add_lint` with an alternative `add_lint_diagnostic` that takes in a full diagnostic (cc @jonathandturner for those changes). This doesn't feel ideal but feels like it's moving in the right direction =).
r? @pnkfelix
cc @arielb1
Fixes #18937
59 files changed, 1713 insertions, 785 deletions
diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 5e3925b0b3c..47c0bc5fd60 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -245,6 +245,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { debug!("report_region_errors: {} errors after preprocessing", errors.len()); for error in errors { + debug!("report_region_errors: error = {:?}", error); match error.clone() { ConcreteFailure(origin, sub, sup) => { self.report_concrete_failure(origin, sub, sup).emit(); @@ -299,44 +300,64 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let mut bound_failures = Vec::new(); for error in errors { + // Check whether we can process this error into some other + // form; if not, fall through. match *error { ConcreteFailure(ref origin, sub, sup) => { debug!("processing ConcreteFailure"); - match free_regions_from_same_fn(self.tcx, sub, sup) { - Some(ref same_frs) => { - origins.push( - ProcessedErrorOrigin::ConcreteFailure( - origin.clone(), - sub, - sup)); - append_to_same_regions(&mut same_regions, same_frs); - } - _ => { - other_errors.push(error.clone()); - } + if let SubregionOrigin::CompareImplMethodObligation { .. } = *origin { + // When comparing an impl method against a + // trait method, it is not helpful to suggest + // changes to the impl method. This is + // because the impl method signature is being + // checked using the trait's environment, so + // usually the changes we suggest would + // actually have to be applied to the *trait* + // method (and it's not clear that the trait + // method is even under the user's control). + } else if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) { + origins.push( + ProcessedErrorOrigin::ConcreteFailure( + origin.clone(), + sub, + sup)); + append_to_same_regions(&mut same_regions, &same_frs); + continue; } } - SubSupConflict(ref var_origin, _, sub_r, _, sup_r) => { - debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub_r, sup_r); - match free_regions_from_same_fn(self.tcx, sub_r, sup_r) { - Some(ref same_frs) => { - origins.push( - ProcessedErrorOrigin::VariableFailure( - var_origin.clone())); - append_to_same_regions(&mut same_regions, same_frs); + SubSupConflict(ref var_origin, ref sub_origin, sub, ref sup_origin, sup) => { + debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub, sup); + match (sub_origin, sup_origin) { + (&SubregionOrigin::CompareImplMethodObligation { .. }, _) => { + // As above, when comparing an impl method + // against a trait method, it is not helpful + // to suggest changes to the impl method. } - None => { - other_errors.push(error.clone()); + (_, &SubregionOrigin::CompareImplMethodObligation { .. }) => { + // See above. + } + _ => { + if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) { + origins.push( + ProcessedErrorOrigin::VariableFailure( + var_origin.clone())); + append_to_same_regions(&mut same_regions, &same_frs); + continue; + } } } } GenericBoundFailure(ref origin, ref kind, region) => { bound_failures.push((origin.clone(), kind.clone(), region)); + continue; } ProcessedErrors(..) => { bug!("should not encounter a `ProcessedErrors` yet: {:?}", error) } } + + // No changes to this error. + other_errors.push(error.clone()); } // ok, let's pull together the errors, sorted in an order that @@ -630,6 +651,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { format!("the associated type `{}`", p), }; + if let SubregionOrigin::CompareImplMethodObligation { + span, item_name, impl_item_def_id, trait_item_def_id, lint_id + } = origin { + self.report_extra_impl_obligation(span, + item_name, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", bound_kind, sub), + lint_id) + .emit(); + return; + } + let mut err = match *sub { ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => { // Does the required lifetime have a nice name we can print? @@ -947,6 +981,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ""); err } + infer::CompareImplMethodObligation { span, + item_name, + impl_item_def_id, + trait_item_def_id, + lint_id } => { + self.report_extra_impl_obligation(span, + item_name, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", sup, sub), + lint_id) + } } } @@ -1792,6 +1838,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { "...so that references are valid when the destructor \ runs"); } + infer::CompareImplMethodObligation { span, .. } => { + err.span_note( + span, + "...so that the definition in impl matches the definition from the trait"); + } } } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 4c097965bb0..35802aef593 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -355,6 +355,19 @@ pub enum SubregionOrigin<'tcx> { // Region constraint arriving from destructor safety SafeDestructor(Span), + + // Comparing the signature and requirements of an impl method against + // the containing trait. + CompareImplMethodObligation { + span: Span, + item_name: ast::Name, + impl_item_def_id: DefId, + trait_item_def_id: DefId, + + // this is `Some(_)` if this error arises from the bug fix for + // #18937. This is a temporary measure. + lint_id: Option<ast::NodeId>, + }, } /// Places that type/region parameters can appear. @@ -1147,16 +1160,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } pub fn region_outlives_predicate(&self, - span: Span, + cause: &traits::ObligationCause<'tcx>, predicate: &ty::PolyRegionOutlivesPredicate<'tcx>) -> UnitResult<'tcx> { self.commit_if_ok(|snapshot| { let (ty::OutlivesPredicate(r_a, r_b), skol_map) = self.skolemize_late_bound_regions(predicate, snapshot); - let origin = RelateRegionParamBound(span); + let origin = + SubregionOrigin::from_obligation_cause(cause, + || RelateRegionParamBound(cause.span)); self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` - self.leak_check(false, span, &skol_map, snapshot)?; + self.leak_check(false, cause.span, &skol_map, snapshot)?; Ok(self.pop_skolemized(skol_map, snapshot)) }) } @@ -1786,6 +1801,32 @@ impl<'tcx> SubregionOrigin<'tcx> { AddrOf(a) => a, AutoBorrow(a) => a, SafeDestructor(a) => a, + CompareImplMethodObligation { span, .. } => span, + } + } + + pub fn from_obligation_cause<F>(cause: &traits::ObligationCause<'tcx>, + default: F) + -> Self + where F: FnOnce() -> Self + { + match cause.code { + traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => + SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span), + + traits::ObligationCauseCode::CompareImplMethodObligation { item_name, + impl_item_def_id, + trait_item_def_id, + lint_id } => + SubregionOrigin::CompareImplMethodObligation { + span: cause.span, + item_name: item_name, + impl_item_def_id: impl_item_def_id, + trait_item_def_id: trait_item_def_id, + lint_id: lint_id, + }, + + _ => default(), } } } diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 3472c77cf42..82a46f76401 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -198,6 +198,12 @@ declare_lint! { "patterns in functions without body were erroneously allowed" } +declare_lint! { + pub EXTRA_REQUIREMENT_IN_IMPL, + Warn, + "detects extra requirements in impls that were erroneously allowed" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -235,7 +241,8 @@ impl LintPass for HardwiredLints { HR_LIFETIME_IN_ASSOC_TYPE, LIFETIME_UNDERSCORE, SAFE_EXTERN_STATICS, - PATTERNS_IN_FNS_WITHOUT_BODY + PATTERNS_IN_FNS_WITHOUT_BODY, + EXTRA_REQUIREMENT_IN_IMPL ) } } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 20463f42d3b..f08aa2eb49f 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -38,11 +38,12 @@ use util::nodemap::FnvHashMap; use std::cmp; use std::default::Default as StdDefault; use std::mem; +use std::fmt; use syntax::attr; use syntax::parse::token::InternedString; use syntax::ast; -use syntax_pos::Span; -use errors::DiagnosticBuilder; +use syntax_pos::{MultiSpan, Span}; +use errors::{self, Diagnostic, DiagnosticBuilder}; use hir; use hir::intravisit as hir_visit; use syntax::visit as ast_visit; @@ -80,6 +81,46 @@ pub struct LintStore { lint_cap: Option<Level>, } +/// When you call `add_lint` on the session, you wind up storing one +/// of these, which records a "potential lint" at a particular point. +#[derive(PartialEq)] +pub struct EarlyLint { + /// what lint is this? (e.g., `dead_code`) + pub id: LintId, + + /// the main message + pub diagnostic: Diagnostic, +} + +impl fmt::Debug for EarlyLint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("EarlyLint") + .field("id", &self.id) + .field("span", &self.diagnostic.span) + .field("diagnostic", &self.diagnostic) + .finish() + } +} + +pub trait IntoEarlyLint { + fn into_early_lint(self, id: LintId) -> EarlyLint; +} + +impl<'a> IntoEarlyLint for (Span, &'a str) { + fn into_early_lint(self, id: LintId) -> EarlyLint { + let (span, msg) = self; + let mut diagnostic = Diagnostic::new(errors::Level::Warning, msg); + diagnostic.set_span(span); + EarlyLint { id: id, diagnostic: diagnostic } + } +} + +impl IntoEarlyLint for Diagnostic { + fn into_early_lint(self, id: LintId) -> EarlyLint { + EarlyLint { id: id, diagnostic: self } + } +} + /// Extra information for a future incompatibility lint. See the call /// to `register_future_incompatible` in `librustc_lint/lib.rs` for /// guidelines. @@ -388,22 +429,24 @@ pub fn gather_attr(attr: &ast::Attribute) /// in trans that run after the main lint pass is finished. Most /// lints elsewhere in the compiler should call /// `Session::add_lint()` instead. -pub fn raw_emit_lint(sess: &Session, - lints: &LintStore, - lint: &'static Lint, - lvlsrc: LevelSource, - span: Option<Span>, - msg: &str) { +pub fn raw_emit_lint<S: Into<MultiSpan>>(sess: &Session, + lints: &LintStore, + lint: &'static Lint, + lvlsrc: LevelSource, + span: Option<S>, + msg: &str) { raw_struct_lint(sess, lints, lint, lvlsrc, span, msg).emit(); } -pub fn raw_struct_lint<'a>(sess: &'a Session, - lints: &LintStore, - lint: &'static Lint, - lvlsrc: LevelSource, - span: Option<Span>, - msg: &str) - -> DiagnosticBuilder<'a> { +pub fn raw_struct_lint<'a, S>(sess: &'a Session, + lints: &LintStore, + lint: &'static Lint, + lvlsrc: LevelSource, + span: Option<S>, + msg: &str) + -> DiagnosticBuilder<'a> + where S: Into<MultiSpan> +{ let (mut level, source) = lvlsrc; if level == Allow { return sess.diagnostic().struct_dummy(); @@ -496,11 +539,11 @@ pub trait LintContext: Sized { raw_emit_lint(&self.sess(), self.lints(), lint, (level, src), span, msg); } - fn lookup(&self, - lint: &'static Lint, - span: Option<Span>, - msg: &str) - -> DiagnosticBuilder { + fn lookup<S: Into<MultiSpan>>(&self, + lint: &'static Lint, + span: Option<S>, + msg: &str) + -> DiagnosticBuilder { let (level, src) = match self.level_src(lint) { None => return self.sess().diagnostic().struct_dummy(), Some(pair) => pair, @@ -514,11 +557,20 @@ pub trait LintContext: Sized { self.lookup_and_emit(lint, Some(span), msg); } - fn struct_span_lint(&self, - lint: &'static Lint, - span: Span, - msg: &str) - -> DiagnosticBuilder { + fn early_lint(&self, early_lint: EarlyLint) { + let span = early_lint.diagnostic.span.primary_span().expect("early lint w/o primary span"); + let mut err = self.struct_span_lint(early_lint.id.lint, + span, + &early_lint.diagnostic.message); + err.copy_details_not_message(&early_lint.diagnostic); + err.emit(); + } + + fn struct_span_lint<S: Into<MultiSpan>>(&self, + lint: &'static Lint, + span: S, + msg: &str) + -> DiagnosticBuilder { self.lookup(lint, Some(span), msg) } @@ -1065,8 +1117,8 @@ impl<'a, 'b, 'tcx, 'v> hir_visit::Visitor<'v> for IdVisitor<'a, 'b, 'tcx> { fn visit_id(&mut self, id: ast::NodeId) { if let Some(lints) = self.cx.sess().lints.borrow_mut().remove(&id) { debug!("LateContext::visit_id: id={:?} lints={:?}", id, lints); - for (lint_id, span, msg) in lints { - self.cx.span_lint(lint_id.lint, span, &msg[..]) + for early_lint in lints { + self.cx.early_lint(early_lint); } } } @@ -1211,10 +1263,10 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // If we missed any lints added to the session, then there's a bug somewhere // in the iteration code. for (id, v) in tcx.sess.lints.borrow().iter() { - for &(lint, span, ref msg) in v { - span_bug!(span, - "unprocessed lint {} at {}: {}", - lint.to_string(), tcx.map.node_to_string(*id), *msg) + for early_lint in v { + span_bug!(early_lint.diagnostic.span.clone(), + "unprocessed lint {:?} at {}", + early_lint, tcx.map.node_to_string(*id)); } } @@ -1229,8 +1281,8 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) { cx.with_lint_attrs(&krate.attrs, |cx| { // Lints may be assigned to the whole crate. if let Some(lints) = cx.sess.lints.borrow_mut().remove(&ast::CRATE_NODE_ID) { - for (lint_id, span, msg) in lints { - cx.span_lint(lint_id.lint, span, &msg[..]) + for early_lint in lints { + cx.early_lint(early_lint); } } @@ -1249,8 +1301,8 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) { // If we missed any lints added to the session, then there's a bug somewhere // in the iteration code. for (_, v) in sess.lints.borrow().iter() { - for &(lint, span, ref msg) in v { - span_bug!(span, "unprocessed lint {}: {}", lint.to_string(), *msg) + for early_lint in v { + span_bug!(early_lint.diagnostic.span.clone(), "unprocessed lint {:?}", early_lint); } } } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 7eea6a2fcf2..34e0ce7da14 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -41,7 +41,7 @@ use hir; pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore, raw_emit_lint, check_crate, check_ast_crate, gather_attrs, - raw_struct_lint, FutureIncompatibleInfo}; + raw_struct_lint, FutureIncompatibleInfo, EarlyLint, IntoEarlyLint}; /// Specification of a single lint. #[derive(Copy, Clone, Debug)] diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 1ce5b223fbe..b4dadbf7961 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -74,7 +74,7 @@ pub struct Session { pub local_crate_source_file: Option<PathBuf>, pub working_dir: PathBuf, pub lint_store: RefCell<lint::LintStore>, - pub lints: RefCell<NodeMap<Vec<(lint::LintId, Span, String)>>>, + pub lints: RefCell<NodeMap<Vec<lint::EarlyLint>>>, /// Set of (LintId, span, message) tuples tracking lint (sub)diagnostics /// that have been set once, but should not be set again, in order to avoid /// redundantly verbose output (Issue #24690). @@ -262,17 +262,26 @@ impl Session { lint: &'static lint::Lint, id: ast::NodeId, sp: Span, - msg: String) { + msg: String) + { + self.add_lint_diagnostic(lint, id, (sp, &msg[..])) + } + pub fn add_lint_diagnostic<M>(&self, + lint: &'static lint::Lint, + id: ast::NodeId, + msg: M) + where M: lint::IntoEarlyLint, + { let lint_id = lint::LintId::of(lint); let mut lints = self.lints.borrow_mut(); + let early_lint = msg.into_early_lint(lint_id); if let Some(arr) = lints.get_mut(&id) { - let tuple = (lint_id, sp, msg); - if !arr.contains(&tuple) { - arr.push(tuple); + if !arr.contains(&early_lint) { + arr.push(early_lint); } return; } - lints.insert(id, vec![(lint_id, sp, msg)]); + lints.insert(id, vec![early_lint]); } pub fn reserve_node_ids(&self, count: usize) -> ast::NodeId { let id = self.next_node_id.get(); diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 9435f96c08a..89c8162456c 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -27,6 +27,7 @@ use super::{ use fmt_macros::{Parser, Piece, Position}; use hir::def_id::DefId; use infer::{self, InferCtxt, TypeOrigin}; +use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL; use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; use ty::error::ExpectedFound; use ty::fast_reject; @@ -36,6 +37,7 @@ use util::nodemap::{FnvHashMap, FnvHashSet}; use std::cmp; use std::fmt; +use syntax::ast; use syntax_pos::Span; use errors::DiagnosticBuilder; @@ -417,6 +419,45 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.report_overflow_error(&cycle[0], false); } + pub fn report_extra_impl_obligation(&self, + error_span: Span, + item_name: ast::Name, + _impl_item_def_id: DefId, + trait_item_def_id: DefId, + requirement: &fmt::Display, + lint_id: Option<ast::NodeId>) // (*) + -> DiagnosticBuilder<'tcx> + { + // (*) This parameter is temporary and used only for phasing + // in the bug fix to #18937. If it is `Some`, it has a kind of + // weird effect -- the diagnostic is reported as a lint, and + // the builder which is returned is marked as canceled. + + let mut err = + struct_span_err!(self.tcx.sess, + error_span, + E0276, + "impl has stricter requirements than trait"); + + if let Some(trait_item_span) = self.tcx.map.span_if_local(trait_item_def_id) { + err.span_label(trait_item_span, + &format!("definition of `{}` from trait", item_name)); + } + + err.span_label( + error_span, + &format!("impl has extra requirement {}", requirement)); + + if let Some(node_id) = lint_id { + self.tcx.sess.add_lint_diagnostic(EXTRA_REQUIREMENT_IN_IMPL, + node_id, + (*err).clone()); + err.cancel(); + } + + err + } + pub fn report_selection_error(&self, obligation: &PredicateObligation<'tcx>, error: &SelectionError<'tcx>) @@ -424,12 +465,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let span = obligation.cause.span; let mut err = match *error { SelectionError::Unimplemented => { - if let ObligationCauseCode::CompareImplMethodObligation = obligation.cause.code { - span_err!( - self.tcx.sess, span, E0276, - "the requirement `{}` appears on the impl \ - method but not on the corresponding trait method", - obligation.predicate); + if let ObligationCauseCode::CompareImplMethodObligation { + item_name, impl_item_def_id, trait_item_def_id, lint_id + } = obligation.cause.code { + self.report_extra_impl_obligation( + span, + item_name, + impl_item_def_id, + trait_item_def_id, + &format!("`{}`", obligation.predicate), + lint_id) + .emit(); return; } else { match obligation.predicate { @@ -492,7 +538,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ty::Predicate::RegionOutlives(ref predicate) => { let predicate = self.resolve_type_vars_if_possible(predicate); - let err = self.region_outlives_predicate(span, + let err = self.region_outlives_predicate(&obligation.cause, &predicate).err().unwrap(); struct_span_err!(self.tcx.sess, span, E0279, "the requirement `{}` is not satisfied (`{}`)", @@ -822,6 +868,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { err.note(&format!("required so that reference `{}` does not outlive its referent", ref_ty)); } + ObligationCauseCode::ObjectTypeBound(object_ty, region) => { + err.note(&format!("required so that the lifetime bound of `{}` for `{}` \ + is satisfied", + region, object_ty)); + } ObligationCauseCode::ItemObligation(item_def_id) => { let item_name = tcx.item_path_str(item_def_id); err.note(&format!("required by `{}`", item_name)); @@ -886,7 +937,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { &parent_predicate, &data.parent_code); } - ObligationCauseCode::CompareImplMethodObligation => { + ObligationCauseCode::CompareImplMethodObligation { .. } => { err.note( &format!("the requirement `{}` appears on the impl method \ but not on the corresponding trait method", diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index ded2fdc58b4..906da429036 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -526,7 +526,7 @@ fn process_predicate<'a, 'gcx, 'tcx>( } ty::Predicate::RegionOutlives(ref binder) => { - match selcx.infcx().region_outlives_predicate(obligation.cause.span, binder) { + match selcx.infcx().region_outlives_predicate(&obligation.cause, binder) { Ok(()) => Ok(Some(Vec::new())), Err(_) => Err(CodeSelectionError(Unimplemented)), } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 7ba10d9c0a5..017b34d914f 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -111,6 +111,9 @@ pub enum ObligationCauseCode<'tcx> { /// A type like `&'a T` is WF only if `T: 'a`. ReferenceOutlivesReferent(Ty<'tcx>), + /// A type like `Box<Foo<'a> + 'b>` is WF only if `'b: 'a`. + ObjectTypeBound(Ty<'tcx>, &'tcx ty::Region), + /// Obligation incurred due to an object cast. ObjectCastObligation(/* Object type */ Ty<'tcx>), @@ -138,7 +141,13 @@ pub enum ObligationCauseCode<'tcx> { ImplDerivedObligation(DerivedObligationCause<'tcx>), - CompareImplMethodObligation, + // error derived when matching traits/impls; see ObligationCause for more details + CompareImplMethodObligation { + item_name: ast::Name, + impl_item_def_id: DefId, + trait_item_def_id: DefId, + lint_id: Option<ast::NodeId>, + }, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 022566642f6..d33e8b5675f 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -175,6 +175,13 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { super::ReferenceOutlivesReferent(ty) => { tcx.lift(&ty).map(super::ReferenceOutlivesReferent) } + super::ObjectTypeBound(ty, r) => { + tcx.lift(&ty).and_then(|ty| { + tcx.lift(&r).and_then(|r| { + Some(super::ObjectTypeBound(ty, r)) + }) + }) + } super::ObjectCastObligation(ty) => { tcx.lift(&ty).map(super::ObjectCastObligation) } @@ -195,8 +202,16 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { super::ImplDerivedObligation(ref cause) => { tcx.lift(cause).map(super::ImplDerivedObligation) } - super::CompareImplMethodObligation => { - Some(super::CompareImplMethodObligation) + super::CompareImplMethodObligation { item_name, + impl_item_def_id, + trait_item_def_id, + lint_id } => { + Some(super::CompareImplMethodObligation { + item_name: item_name, + impl_item_def_id: impl_item_def_id, + trait_item_def_id: trait_item_def_id, + lint_id: lint_id, + }) } } } @@ -459,12 +474,15 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> { super::FieldSized | super::ConstSized | super::SharedStatic | - super::CompareImplMethodObligation => self.clone(), + super::CompareImplMethodObligation { .. } => self.clone(), super::ProjectionWf(proj) => super::ProjectionWf(proj.fold_with(folder)), super::ReferenceOutlivesReferent(ty) => { super::ReferenceOutlivesReferent(ty.fold_with(folder)) } + super::ObjectTypeBound(ty, r) => { + super::ObjectTypeBound(ty.fold_with(folder), r.fold_with(folder)) + } super::ObjectCastObligation(ty) => { super::ObjectCastObligation(ty.fold_with(folder)) } @@ -492,10 +510,11 @@ impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> { super::FieldSized | super::ConstSized | super::SharedStatic | - super::CompareImplMethodObligation => false, + super::CompareImplMethodObligation { .. } => false, super::ProjectionWf(proj) => proj.visit_with(visitor), super::ReferenceOutlivesReferent(ty) => ty.visit_with(visitor), + super::ObjectTypeBound(ty, r) => ty.visit_with(visitor) || r.visit_with(visitor), super::ObjectCastObligation(ty) => ty.visit_with(visitor), super::BuiltinDerivedObligation(ref cause) => cause.visit_with(visitor), super::ImplDerivedObligation(ref cause) => cause.visit_with(visitor) diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs index a0792dcf4dd..a3d974216b6 100644 --- a/src/librustc/traits/util.rs +++ b/src/librustc/traits/util.rs @@ -11,6 +11,7 @@ use hir::def_id::DefId; use ty::subst::{Subst, Substs}; use ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef}; +use ty::outlives::Component; use util::common::ErrorReported; use util::nodemap::FnvHashSet; @@ -166,27 +167,63 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> { ty::Predicate::ClosureKind(..) => { // Nothing to elaborate when waiting for a closure's kind to be inferred. } - ty::Predicate::RegionOutlives(..) | - ty::Predicate::TypeOutlives(..) => { - // Currently, we do not "elaborate" predicates like - // `'a : 'b` or `T : 'a`. We could conceivably do - // more here. For example, - // - // &'a int : 'b - // - // implies that - // - // 'a : 'b - // - // and we could get even more if we took WF - // constraints into account. For example, - // - // &'a &'b int : 'c - // - // implies that + + ty::Predicate::RegionOutlives(..) => { + // Nothing to elaborate from `'a: 'b`. + } + + ty::Predicate::TypeOutlives(ref data) => { + // We know that `T: 'a` for some type `T`. We can + // often elaborate this. For example, if we know that + // `[U]: 'a`, that implies that `U: 'a`. Similarly, if + // we know `&'a U: 'b`, then we know that `'a: 'b` and + // `U: 'b`. // - // 'b : 'a - // 'a : 'c + // We can basically ignore bound regions here. So for + // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to + // `'a: 'b`. + + // Ignore `for<'a> T: 'a` -- we might in the future + // consider this as evidence that `T: 'static`, but + // I'm a bit wary of such constructions and so for now + // I want to be conservative. --nmatsakis + let ty_max = data.skip_binder().0; + let r_min = data.skip_binder().1; + if r_min.is_bound() { + return; + } + + let visited = &mut self.visited; + self.stack.extend( + tcx.outlives_components(ty_max) + .into_iter() + .filter_map(|component| match component { + Component::Region(r) => if r.is_bound() { + None + } else { + Some(ty::Predicate::RegionOutlives( + ty::Binder(ty::OutlivesPredicate(r, r_min)))) + }, + + Component::Param(p) => { + let ty = tcx.mk_param(p.idx, p.name); + Some(ty::Predicate::TypeOutlives( + ty::Binder(ty::OutlivesPredicate(ty, r_min)))) + }, + + Component::UnresolvedInferenceVariable(_) => { + None + }, + + Component::Projection(_) | + Component::EscapingProjection(_) => { + // We can probably do more here. This + // corresponds to a case like `<T as + // Foo<'a>>::U: 'b`. + None + }, + }) + .filter(|p| visited.insert(p))); } } } diff --git a/src/librustc/ty/outlives.rs b/src/librustc/ty/outlives.rs index a4edd3b93c9..51feab9d40c 100644 --- a/src/librustc/ty/outlives.rs +++ b/src/librustc/ty/outlives.rs @@ -12,8 +12,7 @@ // refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that // RFC for reference. -use infer::InferCtxt; -use ty::{self, Ty, TypeFoldable}; +use ty::{self, Ty, TyCtxt, TypeFoldable}; #[derive(Debug)] pub enum Component<'tcx> { @@ -55,9 +54,9 @@ pub enum Component<'tcx> { EscapingProjection(Vec<Component<'tcx>>), } -impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { +impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Returns all the things that must outlive `'a` for the condition - /// `ty0: 'a` to hold. + /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. pub fn outlives_components(&self, ty0: Ty<'tcx>) -> Vec<Component<'tcx>> { let mut components = vec![]; @@ -148,16 +147,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - // If we encounter an inference variable, try to resolve it - // and proceed with resolved version. If we cannot resolve it, - // then record the unresolved variable as a component. - ty::TyInfer(_) => { - let ty = self.resolve_type_vars_if_possible(&ty); - if let ty::TyInfer(infer_ty) = ty.sty { - out.push(Component::UnresolvedInferenceVariable(infer_ty)); - } else { - self.compute_components(ty, out); - } + // We assume that inference variables are fully resolved. + // So, if we encounter an inference variable, just record + // the unresolved variable as a component. + ty::TyInfer(infer_ty) => { + out.push(Component::UnresolvedInferenceVariable(infer_ty)); } // Most types do not introduce any region binders, nor diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index 1135199d225..155fa4989ea 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -178,7 +178,8 @@ pub fn implied_bounds<'a, 'gcx, 'tcx>( match infcx.tcx.no_late_bound_regions(data) { None => vec![], Some(ty::OutlivesPredicate(ty_a, r_b)) => { - let components = infcx.outlives_components(ty_a); + let ty_a = infcx.resolve_type_vars_if_possible(&ty_a); + let components = infcx.tcx.outlives_components(ty_a); implied_bounds_from_components(r_b, components) } }, @@ -497,7 +498,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { let explicit_bound = data.region_bound; for implicit_bound in implicit_bounds { - let cause = self.cause(traits::ReferenceOutlivesReferent(ty)); + let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound)); let outlives = ty::Binder(ty::OutlivesPredicate(explicit_bound, implicit_bound)); self.out.push(traits::Obligation::new(cause, outlives.to_predicate())); } diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs new file mode 100644 index 00000000000..730ca8f9e2e --- /dev/null +++ b/src/librustc_errors/diagnostic.rs @@ -0,0 +1,202 @@ +// Copyright 2012-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. + +use CodeSuggestion; +use Level; +use RenderSpan; +use RenderSpan::Suggestion; +use std::fmt; +use syntax_pos::{MultiSpan, Span}; + +#[must_use] +#[derive(Clone, Debug, PartialEq)] +pub struct Diagnostic { + pub level: Level, + pub message: String, + pub code: Option<String>, + pub span: MultiSpan, + pub children: Vec<SubDiagnostic>, +} + +/// For example a note attached to an error. +#[derive(Clone, Debug, PartialEq)] +pub struct SubDiagnostic { + pub level: Level, + pub message: String, + pub span: MultiSpan, + pub render_span: Option<RenderSpan>, +} + +impl Diagnostic { + pub fn new(level: Level, message: &str) -> Self { + Diagnostic::new_with_code(level, None, message) + } + + pub fn new_with_code(level: Level, code: Option<String>, message: &str) -> Self { + Diagnostic { + level: level, + message: message.to_owned(), + code: code, + span: MultiSpan::new(), + children: vec![], + } + } + + /// Cancel the diagnostic (a structured diagnostic must either be emitted or + /// cancelled or it will panic when dropped). + /// BEWARE: if this DiagnosticBuilder is an error, then creating it will + /// bump the error count on the Handler and cancelling it won't undo that. + /// If you want to decrement the error count you should use `Handler::cancel`. + pub fn cancel(&mut self) { + self.level = Level::Cancelled; + } + + pub fn cancelled(&self) -> bool { + self.level == Level::Cancelled + } + + pub fn is_fatal(&self) -> bool { + self.level == Level::Fatal + } + + /// Add a span/label to be included in the resulting snippet. + /// This is pushed onto the `MultiSpan` that was created when the + /// diagnostic was first built. If you don't call this function at + /// all, and you just supplied a `Span` to create the diagnostic, + /// then the snippet will just include that `Span`, which is + /// called the primary span. + pub fn span_label(&mut self, span: Span, label: &fmt::Display) + -> &mut Self { + self.span.push_span_label(span, format!("{}", label)); + self + } + + pub fn note_expected_found(&mut self, + label: &fmt::Display, + expected: &fmt::Display, + found: &fmt::Display) + -> &mut Self + { + self.note_expected_found_extra(label, expected, found, &"", &"") + } + + pub fn note_expected_found_extra(&mut self, + label: &fmt::Display, + expected: &fmt::Display, + found: &fmt::Display, + expected_extra: &fmt::Display, + found_extra: &fmt::Display) + -> &mut Self + { + // For now, just attach these as notes + self.note(&format!("expected {} `{}`{}", label, expected, expected_extra)); + self.note(&format!(" found {} `{}`{}", label, found, found_extra)); + self + } + + pub fn note(&mut self, msg: &str) -> &mut Self { + self.sub(Level::Note, msg, MultiSpan::new(), None); + self + } + + pub fn span_note<S: Into<MultiSpan>>(&mut self, + sp: S, + msg: &str) + -> &mut Self { + self.sub(Level::Note, msg, sp.into(), None); + self + } + + pub fn warn(&mut self, msg: &str) -> &mut Self { + self.sub(Level::Warning, msg, MultiSpan::new(), None); + self + } + + pub fn span_warn<S: Into<MultiSpan>>(&mut self, + sp: S, + msg: &str) + -> &mut Self { + self.sub(Level::Warning, msg, sp.into(), None); + self + } + + pub fn help(&mut self , msg: &str) -> &mut Self { + self.sub(Level::Help, msg, MultiSpan::new(), None); + self + } + + pub fn span_help<S: Into<MultiSpan>>(&mut self, + sp: S, + msg: &str) + -> &mut Self { + self.sub(Level::Help, msg, sp.into(), None); + self + } + + /// Prints out a message with a suggested edit of the code. + /// + /// See `diagnostic::RenderSpan::Suggestion` for more information. + pub fn span_suggestion<S: Into<MultiSpan>>(&mut self, + sp: S, + msg: &str, + suggestion: String) + -> &mut Self { + self.sub(Level::Help, + msg, + MultiSpan::new(), + Some(Suggestion(CodeSuggestion { + msp: sp.into(), + substitutes: vec![suggestion], + }))); + self + } + + pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { + self.span = sp.into(); + self + } + + pub fn code(&mut self, s: String) -> &mut Self { + self.code = Some(s); + self + } + + pub fn message(&self) -> &str { + &self.message + } + + pub fn level(&self) -> Level { + self.level + } + + /// Used by a lint. Copies over all details *but* the "main + /// message". + pub fn copy_details_not_message(&mut self, from: &Diagnostic) { + self.span = from.span.clone(); + self.code = from.code.clone(); + self.children.extend(from.children.iter().cloned()) + } + + /// Convenience function for internal use, clients should use one of the + /// public methods above. + fn sub(&mut self, + level: Level, + message: &str, + span: MultiSpan, + render_span: Option<RenderSpan>) { + let sub = SubDiagnostic { + level: level, + message: message.to_owned(), + span: span, + render_span: render_span, + }; + self.children.push(sub); + } +} diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs new file mode 100644 index 00000000000..7dfea6b8951 --- /dev/null +++ b/src/librustc_errors/diagnostic_builder.rs @@ -0,0 +1,196 @@ +// Copyright 2012-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. + +use Diagnostic; +use Level; +use Handler; +use std::fmt::{self, Debug}; +use std::ops::{Deref, DerefMut}; +use std::thread::panicking; +use syntax_pos::{MultiSpan, Span}; + +/// Used for emitting structured error messages and other diagnostic information. +#[must_use] +#[derive(Clone)] +pub struct DiagnosticBuilder<'a> { + handler: &'a Handler, + diagnostic: Diagnostic, +} + +/// In general, the `DiagnosticBuilder` uses deref to allow access to +/// the fields and methods of the embedded `diagnostic` in a +/// transparent way. *However,* many of the methods are intended to +/// be used in a chained way, and hence ought to return `self`. In +/// that case, we can't just naively forward to the method on the +/// `diagnostic`, because the return type would be a `&Diagnostic` +/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes +/// it easy to declare such methods on the builder. +macro_rules! forward { + // Forward pattern for &self -> &Self + (pub fn $n:ident(&self, $($name:ident: $ty:ty),*) -> &Self) => { + pub fn $n(&self, $($name: $ty),*) -> &Self { + self.diagnostic.$n($($name),*); + self + } + }; + + // Forward pattern for &mut self -> &mut Self + (pub fn $n:ident(&mut self, $($name:ident: $ty:ty),*) -> &mut Self) => { + pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { + self.diagnostic.$n($($name),*); + self + } + }; + + // Forward pattern for &mut self -> &mut Self, with S: Into<MultiSpan> + // type parameter. No obvious way to make this more generic. + (pub fn $n:ident<S: Into<MultiSpan>>(&mut self, $($name:ident: $ty:ty),*) -> &mut Self) => { + pub fn $n<S: Into<MultiSpan>>(&mut self, $($name: $ty),*) -> &mut Self { + self.diagnostic.$n($($name),*); + self + } + }; +} + +impl<'a> Deref for DiagnosticBuilder<'a> { + type Target = Diagnostic; + + fn deref(&self) -> &Diagnostic { + &self.diagnostic + } +} + +impl<'a> DerefMut for DiagnosticBuilder<'a> { + fn deref_mut(&mut self) -> &mut Diagnostic { + &mut self.diagnostic + } +} + +impl<'a> DiagnosticBuilder<'a> { + /// Emit the diagnostic. + pub fn emit(&mut self) { + if self.cancelled() { + return; + } + + match self.level { + Level::Bug | + Level::Fatal | + Level::PhaseFatal | + Level::Error => { + self.handler.bump_err_count(); + } + + Level::Warning | + Level::Note | + Level::Help | + Level::Cancelled => { + } + } + + self.handler.emitter.borrow_mut().emit(&self); + self.cancel(); + self.handler.panic_if_treat_err_as_bug(); + + // if self.is_fatal() { + // panic!(FatalError); + // } + } + + /// Add a span/label to be included in the resulting snippet. + /// This is pushed onto the `MultiSpan` that was created when the + /// diagnostic was first built. If you don't call this function at + /// all, and you just supplied a `Span` to create the diagnostic, + /// then the snippet will just include that `Span`, which is + /// called the primary span. + forward!(pub fn span_label(&mut self, span: Span, label: &fmt::Display) + -> &mut Self); + + forward!(pub fn note_expected_found(&mut self, + label: &fmt::Display, + expected: &fmt::Display, + found: &fmt::Display) + -> &mut Self); + + forward!(pub fn note_expected_found_extra(&mut self, + label: &fmt::Display, + expected: &fmt::Display, + found: &fmt::Display, + expected_extra: &fmt::Display, + found_extra: &fmt::Display) + -> &mut Self); + + forward!(pub fn note(&mut self, msg: &str) -> &mut Self); + forward!(pub fn span_note<S: Into<MultiSpan>>(&mut self, + sp: S, + msg: &str) + -> &mut Self); + forward!(pub fn warn(&mut self, msg: &str) -> &mut Self); + forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self); + forward!(pub fn help(&mut self , msg: &str) -> &mut Self); + forward!(pub fn span_help<S: Into<MultiSpan>>(&mut self, + sp: S, + msg: &str) + -> &mut Self); + forward!(pub fn span_suggestion<S: Into<MultiSpan>>(&mut self, + sp: S, + msg: &str, + suggestion: String) + -> &mut Self); + forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self); + forward!(pub fn code(&mut self, s: String) -> &mut Self); + + /// Convenience function for internal use, clients should use one of the + /// struct_* methods on Handler. + pub fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { + DiagnosticBuilder::new_with_code(handler, level, None, message) + } + + /// Convenience function for internal use, clients should use one of the + /// struct_* methods on Handler. + pub fn new_with_code(handler: &'a Handler, + level: Level, + code: Option<String>, + message: &str) + -> DiagnosticBuilder<'a> { + DiagnosticBuilder { + handler: handler, + diagnostic: Diagnostic::new_with_code(level, code, message) + } + } + + pub fn into_diagnostic(mut self) -> Diagnostic { + // annoyingly, the Drop impl means we can't actually move + let result = self.diagnostic.clone(); + self.cancel(); + result + } +} + +impl<'a> Debug for DiagnosticBuilder<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.diagnostic.fmt(f) + } +} + +/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or +/// we emit a bug. +impl<'a> Drop for DiagnosticBuilder<'a> { + fn drop(&mut self) { + if !panicking() && !self.cancelled() { + let mut db = DiagnosticBuilder::new(self.handler, + Level::Bug, + "Error constructed but not emitted"); + db.emit(); + panic!(); + } + } +} + diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 25b314256b0..badee66b83d 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -39,15 +39,15 @@ extern crate syntax_pos; pub use emitter::ColorConfig; use self::Level::*; -use self::RenderSpan::*; use emitter::{Emitter, EmitterWriter}; use std::cell::{RefCell, Cell}; use std::{error, fmt}; use std::rc::Rc; -use std::thread::panicking; +pub mod diagnostic; +pub mod diagnostic_builder; pub mod emitter; pub mod snippet; pub mod registry; @@ -57,7 +57,7 @@ mod lock; use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION}; use syntax_pos::MacroBacktrace; -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub enum RenderSpan { /// A FullSpan renders with both with an initial line for the /// message, prefixed by file:linenum, followed by a summary of @@ -71,7 +71,7 @@ pub enum RenderSpan { Suggestion(CodeSuggestion), } -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct CodeSuggestion { pub msp: MultiSpan, pub substitutes: Vec<String>, @@ -211,219 +211,8 @@ impl error::Error for ExplicitBug { } } -/// Used for emitting structured error messages and other diagnostic information. -#[must_use] -#[derive(Clone)] -pub struct DiagnosticBuilder<'a> { - handler: &'a Handler, - pub level: Level, - pub message: String, - pub code: Option<String>, - pub span: MultiSpan, - pub children: Vec<SubDiagnostic>, -} - -/// For example a note attached to an error. -#[derive(Clone)] -pub struct SubDiagnostic { - pub level: Level, - pub message: String, - pub span: MultiSpan, - pub render_span: Option<RenderSpan>, -} - -impl<'a> DiagnosticBuilder<'a> { - /// Emit the diagnostic. - pub fn emit(&mut self) { - if self.cancelled() { - return; - } - - self.handler.emitter.borrow_mut().emit(&self); - self.cancel(); - self.handler.panic_if_treat_err_as_bug(); - - // if self.is_fatal() { - // panic!(FatalError); - // } - } - - /// Cancel the diagnostic (a structured diagnostic must either be emitted or - /// cancelled or it will panic when dropped). - /// BEWARE: if this DiagnosticBuilder is an error, then creating it will - /// bump the error count on the Handler and cancelling it won't undo that. - /// If you want to decrement the error count you should use `Handler::cancel`. - pub fn cancel(&mut self) { - self.level = Level::Cancelled; - } - - pub fn cancelled(&self) -> bool { - self.level == Level::Cancelled - } - - pub fn is_fatal(&self) -> bool { - self.level == Level::Fatal - } - - /// Add a span/label to be included in the resulting snippet. - /// This is pushed onto the `MultiSpan` that was created when the - /// diagnostic was first built. If you don't call this function at - /// all, and you just supplied a `Span` to create the diagnostic, - /// then the snippet will just include that `Span`, which is - /// called the primary span. - pub fn span_label(&mut self, span: Span, label: &fmt::Display) -> &mut DiagnosticBuilder<'a> { - self.span.push_span_label(span, format!("{}", label)); - self - } - - pub fn note_expected_found(&mut self, - label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display) - -> &mut DiagnosticBuilder<'a> { - self.note_expected_found_extra(label, expected, found, &"", &"") - } - - pub fn note_expected_found_extra(&mut self, - label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display, - expected_extra: &fmt::Display, - found_extra: &fmt::Display) - -> &mut DiagnosticBuilder<'a> { - // For now, just attach these as notes - self.note(&format!("expected {} `{}`{}", label, expected, expected_extra)); - self.note(&format!(" found {} `{}`{}", label, found, found_extra)); - self - } - - pub fn note(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, MultiSpan::new(), None); - self - } - pub fn span_note<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, sp.into(), None); - self - } - pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, MultiSpan::new(), None); - self - } - pub fn span_warn<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, sp.into(), None); - self - } - pub fn help(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, MultiSpan::new(), None); - self - } - pub fn span_help<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, sp.into(), None); - self - } - /// Prints out a message with a suggested edit of the code. - /// - /// See `diagnostic::RenderSpan::Suggestion` for more information. - pub fn span_suggestion<S: Into<MultiSpan>>(&mut self, - sp: S, - msg: &str, - suggestion: String) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, - msg, - MultiSpan::new(), - Some(Suggestion(CodeSuggestion { - msp: sp.into(), - substitutes: vec![suggestion], - }))); - self - } - - pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { - self.span = sp.into(); - self - } - - pub fn code(&mut self, s: String) -> &mut Self { - self.code = Some(s); - self - } - - pub fn message(&self) -> &str { - &self.message - } - - pub fn level(&self) -> Level { - self.level - } - - /// Convenience function for internal use, clients should use one of the - /// struct_* methods on Handler. - fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { - DiagnosticBuilder::new_with_code(handler, level, None, message) - } - - /// Convenience function for internal use, clients should use one of the - /// struct_* methods on Handler. - fn new_with_code(handler: &'a Handler, - level: Level, - code: Option<String>, - message: &str) - -> DiagnosticBuilder<'a> { - DiagnosticBuilder { - handler: handler, - level: level, - message: message.to_owned(), - code: code, - span: MultiSpan::new(), - children: vec![], - } - } - - /// Convenience function for internal use, clients should use one of the - /// public methods above. - fn sub(&mut self, - level: Level, - message: &str, - span: MultiSpan, - render_span: Option<RenderSpan>) { - let sub = SubDiagnostic { - level: level, - message: message.to_owned(), - span: span, - render_span: render_span, - }; - self.children.push(sub); - } -} - -impl<'a> fmt::Debug for DiagnosticBuilder<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.message.fmt(f) - } -} - -/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or -/// we emit a bug. -impl<'a> Drop for DiagnosticBuilder<'a> { - fn drop(&mut self) { - if !panicking() && !self.cancelled() { - let mut db = - DiagnosticBuilder::new(self.handler, Bug, "Error constructed but not emitted"); - db.emit(); - panic!(); - } - } -} +pub use diagnostic::{Diagnostic, SubDiagnostic}; +pub use diagnostic_builder::DiagnosticBuilder; /// A handler deals with errors; certain errors /// (fatal, bug, unimpl) may cause immediate exit, @@ -504,7 +293,6 @@ impl Handler { sp: S, msg: &str) -> DiagnosticBuilder<'a> { - self.bump_err_count(); let mut result = DiagnosticBuilder::new(self, Level::Error, msg); result.set_span(sp); result @@ -514,21 +302,18 @@ impl Handler { msg: &str, code: &str) -> DiagnosticBuilder<'a> { - self.bump_err_count(); 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, Level::Error, msg) } pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self, sp: S, msg: &str) -> DiagnosticBuilder<'a> { - self.bump_err_count(); let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg); result.set_span(sp); result @@ -538,24 +323,16 @@ impl Handler { msg: &str, code: &str) -> DiagnosticBuilder<'a> { - self.bump_err_count(); 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, Level::Fatal, msg) } pub fn cancel(&self, err: &mut DiagnosticBuilder) { - if err.level == Level::Error || err.level == Level::Fatal { - self.err_count.set(self.err_count - .get() - .checked_sub(1) - .expect("cancelled an error but err_count is 0")); - } err.cancel(); } @@ -567,7 +344,6 @@ impl Handler { pub fn span_fatal<S: Into<MultiSpan>>(&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; } @@ -577,13 +353,11 @@ impl Handler { code: &str) -> 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<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.emit(&sp.into(), msg, Error); - self.bump_err_count(); self.panic_if_treat_err_as_bug(); } pub fn mut_span_err<'a, S: Into<MultiSpan>>(&'a self, @@ -592,12 +366,10 @@ impl Handler { -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(self, Level::Error, msg); result.set_span(sp); - self.bump_err_count(); result } pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) { self.emit_with_code(&sp.into(), msg, code, Error); - self.bump_err_count(); self.panic_if_treat_err_as_bug(); } pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { @@ -616,7 +388,6 @@ impl Handler { } pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.emit(&sp.into(), msg, Bug); - self.bump_err_count(); } pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { self.emit(&sp.into(), msg, Note); @@ -630,7 +401,6 @@ impl Handler { } let mut db = DiagnosticBuilder::new(self, Fatal, msg); db.emit(); - self.bump_err_count(); FatalError } pub fn err(&self, msg: &str) { @@ -639,7 +409,6 @@ impl Handler { } let mut db = DiagnosticBuilder::new(self, Error, msg); db.emit(); - self.bump_err_count(); } pub fn warn(&self, msg: &str) { let mut db = DiagnosticBuilder::new(self, Warning, msg); diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 6f114e09a6c..c4b631c79b0 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -229,6 +229,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(PATTERNS_IN_FNS_WITHOUT_BODY), reference: "issue #35203 <https://github.com/rust-lang/rust/issues/35203>", }, + FutureIncompatibleInfo { + id: LintId::of(EXTRA_REQUIREMENT_IN_IMPL), + reference: "issue #37166 <https://github.com/rust-lang/rust/issues/37166>", + }, ]); // Register renamed and removed lints diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 25e9f1f522c..6f450f57275 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -8,19 +8,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use middle::free_region::FreeRegionMap; use rustc::infer::{self, InferOk, TypeOrigin}; +use rustc::middle::free_region::FreeRegionMap; use rustc::ty; use rustc::traits::{self, Reveal}; use rustc::ty::error::{ExpectedFound, TypeError}; use rustc::ty::subst::{Subst, Substs}; use rustc::hir::{ImplItemKind, TraitItem_, Ty_}; +use rustc::util::common::ErrorReported; use syntax::ast; use syntax_pos::Span; use CrateCtxt; use super::assoc; +use super::{Inherited, FnCtxt}; /// Checks that a method from an impl conforms to the signature of /// the same method as declared in the trait. @@ -39,188 +41,56 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, impl_m_body_id: ast::NodeId, trait_m: &ty::Method<'tcx>, impl_trait_ref: &ty::TraitRef<'tcx>, - trait_item_span: Option<Span>) { - debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); - - debug!("compare_impl_method: impl_trait_ref (liberated) = {:?}", + trait_item_span: Option<Span>, + old_broken_mode: bool) { + debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); - let tcx = ccx.tcx; - - let trait_to_impl_substs = &impl_trait_ref.substs; - - // Try to give more informative error messages about self typing - // mismatches. Note that any mismatch will also be detected - // below, where we construct a canonical function type that - // includes the self parameter as a normal parameter. It's just - // that the error messages you get out of this code are a bit more - // inscrutable, particularly for cases where one method has no - // self. - match (&trait_m.explicit_self, &impl_m.explicit_self) { - (&ty::ExplicitSelfCategory::Static, &ty::ExplicitSelfCategory::Static) => {} - (&ty::ExplicitSelfCategory::Static, _) => { - let mut err = struct_span_err!(tcx.sess, - impl_m_span, - E0185, - "method `{}` has a `{}` declaration in the impl, but \ - not in the trait", - trait_m.name, - impl_m.explicit_self); - err.span_label(impl_m_span, - &format!("`{}` used in impl", impl_m.explicit_self)); - if let Some(span) = tcx.map.span_if_local(trait_m.def_id) { - err.span_label(span, - &format!("trait declared without `{}`", impl_m.explicit_self)); - } - err.emit(); - return; - } - (_, &ty::ExplicitSelfCategory::Static) => { - let mut err = struct_span_err!(tcx.sess, - impl_m_span, - E0186, - "method `{}` has a `{}` declaration in the trait, but \ - not in the impl", - trait_m.name, - trait_m.explicit_self); - err.span_label(impl_m_span, - &format!("expected `{}` in impl", trait_m.explicit_self)); - if let Some(span) = tcx.map.span_if_local(trait_m.def_id) { - err.span_label(span, &format!("`{}` used in trait", trait_m.explicit_self)); - } - err.emit(); - return; - } - _ => { - // Let the type checker catch other errors below - } + if let Err(ErrorReported) = compare_self_type(ccx, + impl_m, + impl_m_span, + trait_m) { + return; } - let num_impl_m_type_params = impl_m.generics.types.len(); - let num_trait_m_type_params = trait_m.generics.types.len(); - if num_impl_m_type_params != num_trait_m_type_params { - let impl_m_node_id = tcx.map.as_local_node_id(impl_m.def_id).unwrap(); - let span = match tcx.map.expect_impl_item(impl_m_node_id).node { - ImplItemKind::Method(ref impl_m_sig, _) => { - if impl_m_sig.generics.is_parameterized() { - impl_m_sig.generics.span - } else { - impl_m_span - } - } - _ => bug!("{:?} is not a method", impl_m), - }; - - let mut err = struct_span_err!(tcx.sess, - span, - E0049, - "method `{}` has {} type parameter{} but its trait \ - declaration has {} type parameter{}", - trait_m.name, - num_impl_m_type_params, - if num_impl_m_type_params == 1 { "" } else { "s" }, - num_trait_m_type_params, - if num_trait_m_type_params == 1 { - "" - } else { - "s" - }); - - let mut suffix = None; - - if let Some(span) = trait_item_span { - err.span_label(span, - &format!("expected {}", - &if num_trait_m_type_params != 1 { - format!("{} type parameters", num_trait_m_type_params) - } else { - format!("{} type parameter", num_trait_m_type_params) - })); - } else { - suffix = Some(format!(", expected {}", num_trait_m_type_params)); - } - - err.span_label(span, - &format!("found {}{}", - &if num_impl_m_type_params != 1 { - format!("{} type parameters", num_impl_m_type_params) - } else { - format!("1 type parameter") - }, - suffix.as_ref().map(|s| &s[..]).unwrap_or(""))); - - err.emit(); + if let Err(ErrorReported) = compare_number_of_generics(ccx, + impl_m, + impl_m_span, + trait_m, + trait_item_span) { + return; + } + if let Err(ErrorReported) = compare_number_of_method_arguments(ccx, + impl_m, + impl_m_span, + trait_m, + trait_item_span) { return; } - if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() { - let trait_number_args = trait_m.fty.sig.0.inputs.len(); - let impl_number_args = impl_m.fty.sig.0.inputs.len(); - let trait_m_node_id = tcx.map.as_local_node_id(trait_m.def_id); - let trait_span = if let Some(trait_id) = trait_m_node_id { - match tcx.map.expect_trait_item(trait_id).node { - TraitItem_::MethodTraitItem(ref trait_m_sig, _) => { - if let Some(arg) = trait_m_sig.decl.inputs.get(if trait_number_args > 0 { - trait_number_args - 1 - } else { - 0 - }) { - Some(arg.pat.span) - } else { - trait_item_span - } - } - _ => bug!("{:?} is not a method", impl_m), - } - } else { - trait_item_span - }; - let impl_m_node_id = tcx.map.as_local_node_id(impl_m.def_id).unwrap(); - let impl_span = match tcx.map.expect_impl_item(impl_m_node_id).node { - ImplItemKind::Method(ref impl_m_sig, _) => { - if let Some(arg) = impl_m_sig.decl.inputs.get(if impl_number_args > 0 { - impl_number_args - 1 - } else { - 0 - }) { - arg.pat.span - } else { - impl_m_span - } - } - _ => bug!("{:?} is not a method", impl_m), - }; - let mut err = struct_span_err!(tcx.sess, - impl_span, - E0050, - "method `{}` has {} parameter{} but the declaration in \ - trait `{}` has {}", - trait_m.name, - impl_number_args, - if impl_number_args == 1 { "" } else { "s" }, - tcx.item_path_str(trait_m.def_id), - trait_number_args); - if let Some(trait_span) = trait_span { - err.span_label(trait_span, - &format!("trait requires {}", - &if trait_number_args != 1 { - format!("{} parameters", trait_number_args) - } else { - format!("{} parameter", trait_number_args) - })); - } - err.span_label(impl_span, - &format!("expected {}, found {}", - &if trait_number_args != 1 { - format!("{} parameters", trait_number_args) - } else { - format!("{} parameter", trait_number_args) - }, - impl_number_args)); - err.emit(); + if let Err(ErrorReported) = compare_predicate_entailment(ccx, + impl_m, + impl_m_span, + impl_m_body_id, + trait_m, + impl_trait_ref, + old_broken_mode) { return; } +} + +fn compare_predicate_entailment<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + impl_m: &ty::Method<'tcx>, + impl_m_span: Span, + impl_m_body_id: ast::NodeId, + trait_m: &ty::Method<'tcx>, + impl_trait_ref: &ty::TraitRef<'tcx>, + old_broken_mode: bool) + -> Result<(), ErrorReported> { + let tcx = ccx.tcx; + + let trait_to_impl_substs = &impl_trait_ref.substs; // This code is best explained by example. Consider a trait: // @@ -301,50 +171,48 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, debug!("compare_impl_method: trait_to_skol_substs={:?}", trait_to_skol_substs); - // Check region bounds. FIXME(@jroesch) refactor this away when removing - // ParamBounds. - if !check_region_bounds_on_impl_method(ccx, - impl_m_span, - impl_m, - &trait_m.generics, - &impl_m.generics, - trait_to_skol_substs, - impl_to_skol_substs) { - return; - } - - tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|mut infcx| { - let mut fulfillment_cx = traits::FulfillmentContext::new(); - - // Create obligations for each predicate declared by the impl - // definition in the context of the trait's parameter - // environment. We can't just use `impl_env.caller_bounds`, - // however, because we want to replace all late-bound regions with - // region variables. - let impl_predicates = tcx.lookup_predicates(impl_m.predicates.parent.unwrap()); - let mut hybrid_preds = impl_predicates.instantiate(tcx, impl_to_skol_substs); - - debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds); - - // This is the only tricky bit of the new way we check implementation methods - // We need to build a set of predicates where only the method-level bounds - // are from the trait and we assume all other bounds from the implementation - // to be previously satisfied. - // - // We then register the obligations from the impl_m and check to see - // if all constraints hold. - hybrid_preds.predicates - .extend(trait_m.predicates.instantiate_own(tcx, trait_to_skol_substs).predicates); - - // Construct trait parameter environment and then shift it into the skolemized viewpoint. - // The key step here is to update the caller_bounds's predicates to be - // the new hybrid bounds we computed. - let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_body_id); - let trait_param_env = impl_param_env.with_caller_bounds(hybrid_preds.predicates); - let trait_param_env = - traits::normalize_param_env_or_error(tcx, trait_param_env, normalize_cause.clone()); - // FIXME(@jroesch) this seems ugly, but is a temporary change - infcx.parameter_environment = trait_param_env; + // Check region bounds. + check_region_bounds_on_impl_method(ccx, + impl_m_span, + impl_m, + &trait_m.generics, + &impl_m.generics, + trait_to_skol_substs, + impl_to_skol_substs)?; + + // Create obligations for each predicate declared by the impl + // definition in the context of the trait's parameter + // environment. We can't just use `impl_env.caller_bounds`, + // however, because we want to replace all late-bound regions with + // region variables. + let impl_predicates = tcx.lookup_predicates(impl_m.predicates.parent.unwrap()); + let mut hybrid_preds = impl_predicates.instantiate(tcx, impl_to_skol_substs); + + debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds); + + // This is the only tricky bit of the new way we check implementation methods + // We need to build a set of predicates where only the method-level bounds + // are from the trait and we assume all other bounds from the implementation + // to be previously satisfied. + // + // We then register the obligations from the impl_m and check to see + // if all constraints hold. + hybrid_preds.predicates + .extend(trait_m.predicates.instantiate_own(tcx, trait_to_skol_substs).predicates); + + // Construct trait parameter environment and then shift it into the skolemized viewpoint. + // The key step here is to update the caller_bounds's predicates to be + // the new hybrid bounds we computed. + let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_body_id); + let trait_param_env = impl_param_env.with_caller_bounds(hybrid_preds.predicates); + let trait_param_env = traits::normalize_param_env_or_error(tcx, + trait_param_env, + normalize_cause.clone()); + + tcx.infer_ctxt(None, Some(trait_param_env), Reveal::NotSpecializable).enter(|infcx| { + let inh = Inherited::new(ccx, infcx); + let infcx = &inh.infcx; + let fulfillment_cx = &inh.fulfillment_cx; debug!("compare_impl_method: caller_bounds={:?}", infcx.parameter_environment.caller_bounds); @@ -362,10 +230,15 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let cause = traits::ObligationCause { span: impl_m_span, body_id: impl_m_body_id, - code: traits::ObligationCauseCode::CompareImplMethodObligation, + code: traits::ObligationCauseCode::CompareImplMethodObligation { + item_name: impl_m.name, + impl_item_def_id: impl_m.def_id, + trait_item_def_id: trait_m.def_id, + lint_id: if !old_broken_mode { Some(impl_m_body_id) } else { None }, + }, }; - fulfillment_cx.register_predicate_obligation( + fulfillment_cx.borrow_mut().register_predicate_obligation( &infcx, traits::Obligation::new(cause, predicate)); } @@ -387,15 +260,18 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let tcx = infcx.tcx; let origin = TypeOrigin::MethodCompatCheck(impl_m_span); - let (impl_sig, _) = infcx.replace_late_bound_regions_with_fresh_var(impl_m_span, - infer::HigherRankedType, - &impl_m.fty.sig); - let impl_sig = impl_sig.subst(tcx, impl_to_skol_substs); - let impl_sig = assoc::normalize_associated_types_in(&infcx, - &mut fulfillment_cx, - impl_m_span, - impl_m_body_id, - &impl_sig); + let (impl_sig, _) = + infcx.replace_late_bound_regions_with_fresh_var(impl_m_span, + infer::HigherRankedType, + &impl_m.fty.sig); + let impl_sig = + impl_sig.subst(tcx, impl_to_skol_substs); + let impl_sig = + assoc::normalize_associated_types_in(&infcx, + &mut fulfillment_cx.borrow_mut(), + impl_m_span, + impl_m_body_id, + &impl_sig); let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy { unsafety: impl_m.fty.unsafety, abi: impl_m.fty.abi, @@ -403,14 +279,17 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, })); debug!("compare_impl_method: impl_fty={:?}", impl_fty); - let trait_sig = tcx.liberate_late_bound_regions(infcx.parameter_environment.free_id_outlive, - &trait_m.fty.sig); - let trait_sig = trait_sig.subst(tcx, trait_to_skol_substs); - let trait_sig = assoc::normalize_associated_types_in(&infcx, - &mut fulfillment_cx, - impl_m_span, - impl_m_body_id, - &trait_sig); + let trait_sig = tcx.liberate_late_bound_regions( + infcx.parameter_environment.free_id_outlive, + &trait_m.fty.sig); + let trait_sig = + trait_sig.subst(tcx, trait_to_skol_substs); + let trait_sig = + assoc::normalize_associated_types_in(&infcx, + &mut fulfillment_cx.borrow_mut(), + impl_m_span, + impl_m_body_id, + &trait_sig); let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy { unsafety: trait_m.fty.unsafety, abi: trait_m.fty.abi, @@ -449,171 +328,377 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, })), &terr); diag.emit(); - return; + return Err(ErrorReported); } // Check that all obligations are satisfied by the implementation's // version. - if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) { + if let Err(ref errors) = fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { infcx.report_fulfillment_errors(errors); - return; + return Err(ErrorReported); } // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. We have to build up a plausible lifetime - // environment based on what we find in the trait. We could also - // include the obligations derived from the method argument types, - // but I don't think it's necessary -- after all, those are still - // in effect when type-checking the body, and all the - // where-clauses in the header etc should be implied by the trait - // anyway, so it shouldn't be needed there either. Anyway, we can - // always add more relations later (it's backwards compat). - let mut free_regions = FreeRegionMap::new(); - free_regions.relate_free_regions_from_predicates( - &infcx.parameter_environment.caller_bounds); - - infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id); - }); + // lifetime parameters. + if old_broken_mode { + // FIXME(#18937) -- this is how the code used to + // work. This is buggy because the fulfillment cx creates + // region obligations that get overlooked. The right + // thing to do is the code below. But we keep this old + // pass around temporarily. + let mut free_regions = FreeRegionMap::new(); + free_regions.relate_free_regions_from_predicates( + &infcx.parameter_environment.caller_bounds); + infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id); + } else { + let fcx = FnCtxt::new(&inh, tcx.types.err, impl_m_body_id); + fcx.regionck_item(impl_m_body_id, impl_m_span, &[]); + } + + Ok(()) + }) +} - fn check_region_bounds_on_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, - span: Span, - impl_m: &ty::Method<'tcx>, - trait_generics: &ty::Generics<'tcx>, - impl_generics: &ty::Generics<'tcx>, - trait_to_skol_substs: &Substs<'tcx>, - impl_to_skol_substs: &Substs<'tcx>) - -> bool { - - let trait_params = &trait_generics.regions[..]; - let impl_params = &impl_generics.regions[..]; - - debug!("check_region_bounds_on_impl_method: \ - trait_generics={:?} \ - impl_generics={:?} \ - trait_to_skol_substs={:?} \ - impl_to_skol_substs={:?}", - trait_generics, - impl_generics, - trait_to_skol_substs, - impl_to_skol_substs); - - // Must have same number of early-bound lifetime parameters. - // Unfortunately, if the user screws up the bounds, then this - // will change classification between early and late. E.g., - // if in trait we have `<'a,'b:'a>`, and in impl we just have - // `<'a,'b>`, then we have 2 early-bound lifetime parameters - // in trait but 0 in the impl. But if we report "expected 2 - // but found 0" it's confusing, because it looks like there - // are zero. Since I don't quite know how to phrase things at - // the moment, give a kind of vague error message. - if trait_params.len() != impl_params.len() { - struct_span_err!(ccx.tcx.sess, - span, - E0195, - "lifetime parameters or bounds on method `{}` do not match the \ - trait declaration", - impl_m.name) - .span_label(span, &format!("lifetimes do not match trait")) - .emit(); - return false; +fn check_region_bounds_on_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + span: Span, + impl_m: &ty::Method<'tcx>, + trait_generics: &ty::Generics<'tcx>, + impl_generics: &ty::Generics<'tcx>, + trait_to_skol_substs: &Substs<'tcx>, + impl_to_skol_substs: &Substs<'tcx>) + -> Result<(), ErrorReported> { + let trait_params = &trait_generics.regions[..]; + let impl_params = &impl_generics.regions[..]; + + debug!("check_region_bounds_on_impl_method: \ + trait_generics={:?} \ + impl_generics={:?} \ + trait_to_skol_substs={:?} \ + impl_to_skol_substs={:?}", + trait_generics, + impl_generics, + trait_to_skol_substs, + impl_to_skol_substs); + + // Must have same number of early-bound lifetime parameters. + // Unfortunately, if the user screws up the bounds, then this + // will change classification between early and late. E.g., + // if in trait we have `<'a,'b:'a>`, and in impl we just have + // `<'a,'b>`, then we have 2 early-bound lifetime parameters + // in trait but 0 in the impl. But if we report "expected 2 + // but found 0" it's confusing, because it looks like there + // are zero. Since I don't quite know how to phrase things at + // the moment, give a kind of vague error message. + if trait_params.len() != impl_params.len() { + struct_span_err!(ccx.tcx.sess, + span, + E0195, + "lifetime parameters or bounds on method `{}` do not match the \ + trait declaration", + impl_m.name) + .span_label(span, &format!("lifetimes do not match trait")) + .emit(); + return Err(ErrorReported); + } + + return Ok(()); +} + +fn extract_spans_for_error_reporting<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, + terr: &TypeError, + origin: TypeOrigin, + impl_m: &ty::Method, + impl_sig: ty::FnSig<'tcx>, + trait_m: &ty::Method, + trait_sig: ty::FnSig<'tcx>) + -> (Span, Option<Span>) { + let tcx = infcx.tcx; + let impl_m_node_id = tcx.map.as_local_node_id(impl_m.def_id).unwrap(); + let (impl_m_output, impl_m_iter) = match tcx.map.expect_impl_item(impl_m_node_id).node { + ImplItemKind::Method(ref impl_m_sig, _) => { + (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()) + } + _ => bug!("{:?} is not a method", impl_m), + }; + + match *terr { + TypeError::Mutability => { + if let Some(trait_m_node_id) = tcx.map.as_local_node_id(trait_m.def_id) { + let trait_m_iter = match tcx.map.expect_trait_item(trait_m_node_id).node { + TraitItem_::MethodTraitItem(ref trait_m_sig, _) => { + trait_m_sig.decl.inputs.iter() + } + _ => bug!("{:?} is not a MethodTraitItem", trait_m), + }; + + impl_m_iter.zip(trait_m_iter) + .find(|&(ref impl_arg, ref trait_arg)| { + match (&impl_arg.ty.node, &trait_arg.ty.node) { + (&Ty_::TyRptr(_, ref impl_mt), &Ty_::TyRptr(_, ref trait_mt)) | + (&Ty_::TyPtr(ref impl_mt), &Ty_::TyPtr(ref trait_mt)) => { + impl_mt.mutbl != trait_mt.mutbl + } + _ => false, + } + }) + .map(|(ref impl_arg, ref trait_arg)| { + match (impl_arg.to_self(), trait_arg.to_self()) { + (Some(impl_self), Some(trait_self)) => { + (impl_self.span, Some(trait_self.span)) + } + (None, None) => (impl_arg.ty.span, Some(trait_arg.ty.span)), + _ => { + bug!("impl and trait fns have different first args, impl: \ + {:?}, trait: {:?}", + impl_arg, + trait_arg) + } + } + }) + .unwrap_or((origin.span(), tcx.map.span_if_local(trait_m.def_id))) + } else { + (origin.span(), tcx.map.span_if_local(trait_m.def_id)) + } } + TypeError::Sorts(ExpectedFound { .. }) => { + if let Some(trait_m_node_id) = tcx.map.as_local_node_id(trait_m.def_id) { + let (trait_m_output, trait_m_iter) = + match tcx.map.expect_trait_item(trait_m_node_id).node { + TraitItem_::MethodTraitItem(ref trait_m_sig, _) => { + (&trait_m_sig.decl.output, trait_m_sig.decl.inputs.iter()) + } + _ => bug!("{:?} is not a MethodTraitItem", trait_m), + }; - return true; + let impl_iter = impl_sig.inputs.iter(); + let trait_iter = trait_sig.inputs.iter(); + impl_iter.zip(trait_iter) + .zip(impl_m_iter) + .zip(trait_m_iter) + .filter_map(|(((impl_arg_ty, trait_arg_ty), impl_arg), trait_arg)| { + match infcx.sub_types(true, origin, trait_arg_ty, impl_arg_ty) { + Ok(_) => None, + Err(_) => Some((impl_arg.ty.span, Some(trait_arg.ty.span))), + } + }) + .next() + .unwrap_or_else(|| { + if infcx.sub_types(false, origin, impl_sig.output, trait_sig.output) + .is_err() { + (impl_m_output.span(), Some(trait_m_output.span())) + } else { + (origin.span(), tcx.map.span_if_local(trait_m.def_id)) + } + }) + } else { + (origin.span(), tcx.map.span_if_local(trait_m.def_id)) + } + } + _ => (origin.span(), tcx.map.span_if_local(trait_m.def_id)), } +} - fn extract_spans_for_error_reporting<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, - terr: &TypeError, - origin: TypeOrigin, - impl_m: &ty::Method, - impl_sig: ty::FnSig<'tcx>, - trait_m: &ty::Method, - trait_sig: ty::FnSig<'tcx>) - -> (Span, Option<Span>) { - let tcx = infcx.tcx; +fn compare_self_type<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + impl_m: &ty::Method<'tcx>, + impl_m_span: Span, + trait_m: &ty::Method<'tcx>) + -> Result<(), ErrorReported> +{ + let tcx = ccx.tcx; + // Try to give more informative error messages about self typing + // mismatches. Note that any mismatch will also be detected + // below, where we construct a canonical function type that + // includes the self parameter as a normal parameter. It's just + // that the error messages you get out of this code are a bit more + // inscrutable, particularly for cases where one method has no + // self. + match (&trait_m.explicit_self, &impl_m.explicit_self) { + (&ty::ExplicitSelfCategory::Static, &ty::ExplicitSelfCategory::Static) => {} + (&ty::ExplicitSelfCategory::Static, _) => { + let mut err = struct_span_err!(tcx.sess, + impl_m_span, + E0185, + "method `{}` has a `{}` declaration in the impl, but \ + not in the trait", + trait_m.name, + impl_m.explicit_self); + err.span_label(impl_m_span, + &format!("`{}` used in impl", impl_m.explicit_self)); + if let Some(span) = tcx.map.span_if_local(trait_m.def_id) { + err.span_label(span, + &format!("trait declared without `{}`", impl_m.explicit_self)); + } + err.emit(); + return Err(ErrorReported); + } + (_, &ty::ExplicitSelfCategory::Static) => { + let mut err = struct_span_err!(tcx.sess, + impl_m_span, + E0186, + "method `{}` has a `{}` declaration in the trait, but \ + not in the impl", + trait_m.name, + trait_m.explicit_self); + err.span_label(impl_m_span, + &format!("expected `{}` in impl", trait_m.explicit_self)); + if let Some(span) = tcx.map.span_if_local(trait_m.def_id) { + err.span_label(span, &format!("`{}` used in trait", trait_m.explicit_self)); + } + err.emit(); + return Err(ErrorReported); + } + _ => { + // Let the type checker catch other errors below + } + } + + Ok(()) +} + +fn compare_number_of_generics<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + impl_m: &ty::Method<'tcx>, + impl_m_span: Span, + trait_m: &ty::Method<'tcx>, + trait_item_span: Option<Span>) + -> Result<(), ErrorReported> { + let tcx = ccx.tcx; + let num_impl_m_type_params = impl_m.generics.types.len(); + let num_trait_m_type_params = trait_m.generics.types.len(); + if num_impl_m_type_params != num_trait_m_type_params { let impl_m_node_id = tcx.map.as_local_node_id(impl_m.def_id).unwrap(); - let (impl_m_output, impl_m_iter) = match tcx.map.expect_impl_item(impl_m_node_id).node { + let span = match tcx.map.expect_impl_item(impl_m_node_id).node { ImplItemKind::Method(ref impl_m_sig, _) => { - (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()) + if impl_m_sig.generics.is_parameterized() { + impl_m_sig.generics.span + } else { + impl_m_span + } } _ => bug!("{:?} is not a method", impl_m), }; - match *terr { - TypeError::Mutability => { - if let Some(trait_m_node_id) = tcx.map.as_local_node_id(trait_m.def_id) { - let trait_m_iter = match tcx.map.expect_trait_item(trait_m_node_id).node { - TraitItem_::MethodTraitItem(ref trait_m_sig, _) => { - trait_m_sig.decl.inputs.iter() - } - _ => bug!("{:?} is not a MethodTraitItem", trait_m), - }; + let mut err = struct_span_err!(tcx.sess, + span, + E0049, + "method `{}` has {} type parameter{} but its trait \ + declaration has {} type parameter{}", + trait_m.name, + num_impl_m_type_params, + if num_impl_m_type_params == 1 { "" } else { "s" }, + num_trait_m_type_params, + if num_trait_m_type_params == 1 { + "" + } else { + "s" + }); - impl_m_iter.zip(trait_m_iter) - .find(|&(ref impl_arg, ref trait_arg)| { - match (&impl_arg.ty.node, &trait_arg.ty.node) { - (&Ty_::TyRptr(_, ref impl_mt), &Ty_::TyRptr(_, ref trait_mt)) | - (&Ty_::TyPtr(ref impl_mt), &Ty_::TyPtr(ref trait_mt)) => { - impl_mt.mutbl != trait_mt.mutbl - } - _ => false, - } - }) - .map(|(ref impl_arg, ref trait_arg)| { - match (impl_arg.to_self(), trait_arg.to_self()) { - (Some(impl_self), Some(trait_self)) => { - (impl_self.span, Some(trait_self.span)) - } - (None, None) => (impl_arg.ty.span, Some(trait_arg.ty.span)), - _ => { - bug!("impl and trait fns have different first args, impl: \ - {:?}, trait: {:?}", - impl_arg, - trait_arg) - } - } - }) - .unwrap_or((origin.span(), tcx.map.span_if_local(trait_m.def_id))) - } else { - (origin.span(), tcx.map.span_if_local(trait_m.def_id)) + let mut suffix = None; + + if let Some(span) = trait_item_span { + err.span_label(span, + &format!("expected {}", + &if num_trait_m_type_params != 1 { + format!("{} type parameters", num_trait_m_type_params) + } else { + format!("{} type parameter", num_trait_m_type_params) + })); + } else { + suffix = Some(format!(", expected {}", num_trait_m_type_params)); + } + + err.span_label(span, + &format!("found {}{}", + &if num_impl_m_type_params != 1 { + format!("{} type parameters", num_impl_m_type_params) + } else { + format!("1 type parameter") + }, + suffix.as_ref().map(|s| &s[..]).unwrap_or(""))); + + err.emit(); + + return Err(ErrorReported); + } + + Ok(()) +} + +fn compare_number_of_method_arguments<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + impl_m: &ty::Method<'tcx>, + impl_m_span: Span, + trait_m: &ty::Method<'tcx>, + trait_item_span: Option<Span>) + -> Result<(), ErrorReported> { + let tcx = ccx.tcx; + if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() { + let trait_number_args = trait_m.fty.sig.0.inputs.len(); + let impl_number_args = impl_m.fty.sig.0.inputs.len(); + let trait_m_node_id = tcx.map.as_local_node_id(trait_m.def_id); + let trait_span = if let Some(trait_id) = trait_m_node_id { + match tcx.map.expect_trait_item(trait_id).node { + TraitItem_::MethodTraitItem(ref trait_m_sig, _) => { + if let Some(arg) = trait_m_sig.decl.inputs.get(if trait_number_args > 0 { + trait_number_args - 1 + } else { + 0 + }) { + Some(arg.pat.span) + } else { + trait_item_span + } } + _ => bug!("{:?} is not a method", impl_m), } - TypeError::Sorts(ExpectedFound { .. }) => { - if let Some(trait_m_node_id) = tcx.map.as_local_node_id(trait_m.def_id) { - let (trait_m_output, trait_m_iter) = - match tcx.map.expect_trait_item(trait_m_node_id).node { - TraitItem_::MethodTraitItem(ref trait_m_sig, _) => { - (&trait_m_sig.decl.output, trait_m_sig.decl.inputs.iter()) - } - _ => bug!("{:?} is not a MethodTraitItem", trait_m), - }; - - let impl_iter = impl_sig.inputs.iter(); - let trait_iter = trait_sig.inputs.iter(); - impl_iter.zip(trait_iter) - .zip(impl_m_iter) - .zip(trait_m_iter) - .filter_map(|(((impl_arg_ty, trait_arg_ty), impl_arg), trait_arg)| { - match infcx.sub_types(true, origin, trait_arg_ty, impl_arg_ty) { - Ok(_) => None, - Err(_) => Some((impl_arg.ty.span, Some(trait_arg.ty.span))), - } - }) - .next() - .unwrap_or_else(|| { - if infcx.sub_types(false, origin, impl_sig.output, trait_sig.output) - .is_err() { - (impl_m_output.span(), Some(trait_m_output.span())) - } else { - (origin.span(), tcx.map.span_if_local(trait_m.def_id)) - } - }) + } else { + trait_item_span + }; + let impl_m_node_id = tcx.map.as_local_node_id(impl_m.def_id).unwrap(); + let impl_span = match tcx.map.expect_impl_item(impl_m_node_id).node { + ImplItemKind::Method(ref impl_m_sig, _) => { + if let Some(arg) = impl_m_sig.decl.inputs.get(if impl_number_args > 0 { + impl_number_args - 1 } else { - (origin.span(), tcx.map.span_if_local(trait_m.def_id)) + 0 + }) { + arg.pat.span + } else { + impl_m_span } } - _ => (origin.span(), tcx.map.span_if_local(trait_m.def_id)), + _ => bug!("{:?} is not a method", impl_m), + }; + let mut err = struct_span_err!(tcx.sess, + impl_span, + E0050, + "method `{}` has {} parameter{} but the declaration in \ + trait `{}` has {}", + trait_m.name, + impl_number_args, + if impl_number_args == 1 { "" } else { "s" }, + tcx.item_path_str(trait_m.def_id), + trait_number_args); + if let Some(trait_span) = trait_span { + err.span_label(trait_span, + &format!("trait requires {}", + &if trait_number_args != 1 { + format!("{} parameters", trait_number_args) + } else { + format!("{} parameter", trait_number_args) + })); } + err.span_label(impl_span, + &format!("expected {}, found {}", + &if trait_number_args != 1 { + format!("{} parameters", trait_number_args) + } else { + format!("{} parameter", trait_number_args) + }, + impl_number_args)); + err.emit(); + return Err(ErrorReported); } + + Ok(()) } pub fn compare_const_impl<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 75a14bb3db9..4e0f455e362 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -407,22 +407,26 @@ impl<'a, 'gcx, 'tcx> InheritedBuilder<'a, 'gcx, 'tcx> { where F: for<'b> FnOnce(Inherited<'b, 'gcx, 'tcx>) -> R { let ccx = self.ccx; - self.infcx.enter(|infcx| { - f(Inherited { - ccx: ccx, - infcx: infcx, - fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), - locals: RefCell::new(NodeMap()), - deferred_call_resolutions: RefCell::new(DefIdMap()), - deferred_cast_checks: RefCell::new(Vec::new()), - anon_types: RefCell::new(DefIdMap()), - deferred_obligations: RefCell::new(Vec::new()), - }) - }) + self.infcx.enter(|infcx| f(Inherited::new(ccx, infcx))) } } impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { + pub fn new(ccx: &'a CrateCtxt<'a, 'gcx>, + infcx: InferCtxt<'a, 'gcx, 'tcx>) + -> Self { + Inherited { + ccx: ccx, + infcx: infcx, + fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), + locals: RefCell::new(NodeMap()), + deferred_call_resolutions: RefCell::new(DefIdMap()), + deferred_cast_checks: RefCell::new(Vec::new()), + anon_types: RefCell::new(DefIdMap()), + deferred_obligations: RefCell::new(Vec::new()), + } + } + fn normalize_associated_types_in<T>(&self, span: Span, body_id: ast::NodeId, @@ -1024,13 +1028,26 @@ fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let trait_span = tcx.map.span_if_local(ty_trait_item.def_id()); if let &ty::MethodTraitItem(ref trait_method) = ty_trait_item { + let err_count = tcx.sess.err_count(); compare_impl_method(ccx, &impl_method, impl_item.span, body.id, &trait_method, &impl_trait_ref, - trait_span); + trait_span, + true); // start with old-broken-mode + if err_count == tcx.sess.err_count() { + // old broken mode did not report an error. Try with the new mode. + compare_impl_method(ccx, + &impl_method, + impl_item.span, + body.id, + &trait_method, + &impl_trait_ref, + trait_span, + false); // use the new mode + } } else { let mut err = struct_span_err!(tcx.sess, impl_item.span, E0324, "item `{}` is an associated method, \ diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 939deee27c6..9bfe80dba9d 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -356,7 +356,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("visit_region_obligations: r_o={:?} cause={:?}", r_o, r_o.cause); let sup_type = self.resolve_type(r_o.sup_type); - let origin = self.code_to_origin(r_o.cause.span, sup_type, &r_o.cause.code); + let origin = self.code_to_origin(&r_o.cause, sup_type); self.type_must_outlive(origin, sup_type, r_o.sub_region); } @@ -366,16 +366,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } fn code_to_origin(&self, - span: Span, - sup_type: Ty<'tcx>, - code: &traits::ObligationCauseCode<'tcx>) + cause: &traits::ObligationCause<'tcx>, + sup_type: Ty<'tcx>) -> SubregionOrigin<'tcx> { - match *code { - traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => - infer::ReferenceOutlivesReferent(ref_type, span), - _ => - infer::RelateParamBound(span, sup_type), - } + SubregionOrigin::from_obligation_cause(cause, + || infer::RelateParamBound(cause.span, sup_type)) } /// This method populates the region map's `free_region_map`. It walks over the transformed @@ -1474,7 +1469,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { assert!(!ty.has_escaping_regions()); - let components = self.outlives_components(ty); + let components = self.tcx.outlives_components(ty); self.components_must_outlive(origin, components, region); } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 7dd850180d4..be012d8976f 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -458,7 +458,7 @@ Rust only supports variadic parameters for interoperability with C code in its FFI. As such, variadic parameters can only be used with functions which are using the C ABI. Examples of erroneous code: -```compile_fail,E0045 +```compile_fail #![feature(unboxed_closures)] extern "rust-call" { fn foo(x: u8, ...); } diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index d83d3a6c5cf..d99850332c3 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -67,7 +67,7 @@ pub struct Span { /// the error, and would be rendered with `^^^`. /// - they can have a *label*. In this case, the label is written next /// to the mark in the snippet when we render. -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct MultiSpan { primary_spans: Vec<Span>, span_labels: Vec<(Span, String)>, diff --git a/src/test/compile-fail/issue-14853.rs b/src/test/compile-fail/issue-14853.rs index c4d88267032..e4da3e4fa43 100644 --- a/src/test/compile-fail/issue-14853.rs +++ b/src/test/compile-fail/issue-14853.rs @@ -20,7 +20,7 @@ struct X { data: u32 } impl Something for X { fn yay<T: Str>(_:Option<X>, thing: &[T]) { - //~^ ERROR the requirement `T: Str` appears on the impl method + //~^ ERROR E0276 } } diff --git a/src/test/compile-fail/issue-18937.rs b/src/test/compile-fail/issue-18937.rs new file mode 100644 index 00000000000..8ac66bb44d2 --- /dev/null +++ b/src/test/compile-fail/issue-18937.rs @@ -0,0 +1,53 @@ +// 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. + +// Regression test for #18937. + +#![deny(extra_requirement_in_impl)] + +use std::fmt; + +#[derive(Debug)] +struct MyString<'a>(&'a String); + +struct B { + list: Vec<Box<fmt::Debug>>, +} + +trait A<'a> { + fn foo<F>(&mut self, f: F) + where F: fmt::Debug + 'a, + Self: Sized; +} + +impl<'a> A<'a> for B { + fn foo<F>(&mut self, f: F) //~ ERROR E0276 + //~^ WARNING future release + where F: fmt::Debug + 'static, + { + self.list.push(Box::new(f)); + } +} + +fn main() { + let mut b = B { list: Vec::new() }; + + // Create a borrowed pointer, put it in `b`, then drop what's borrowing it + let a = "hello".to_string(); + b.foo(MyString(&a)); + + // Drop the data which `b` has a reference to + drop(a); + + // Use the data, probably segfaulting + for b in b.list.iter() { + println!("{:?}", b); + } +} diff --git a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs index 6e60a373d9b..1d4ffe0690d 100644 --- a/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs +++ b/src/test/compile-fail/regions-bound-missing-bound-in-impl.rs @@ -52,7 +52,7 @@ impl<'a, 't> Foo<'a, 't> for &'a isize { } fn another_bound<'x: 't>(self, x: Inv<'x>, y: Inv<'t>) { - //~^ ERROR lifetime bound not satisfied + //~^ ERROR E0276 } } diff --git a/src/test/parse-fail/associated-types-project-from-hrtb-explicit.rs b/src/test/parse-fail/associated-types-project-from-hrtb-explicit.rs index 9c7721589d9..70055a10181 100644 --- a/src/test/parse-fail/associated-types-project-from-hrtb-explicit.rs +++ b/src/test/parse-fail/associated-types-project-from-hrtb-explicit.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error // Test you can't use a higher-ranked trait bound inside of a qualified // path (just won't parse). diff --git a/src/test/parse-fail/generic-non-trailing-defaults.rs b/src/test/parse-fail/generic-non-trailing-defaults.rs index 26ee6ce80d6..2bb593258ae 100644 --- a/src/test/parse-fail/generic-non-trailing-defaults.rs +++ b/src/test/parse-fail/generic-non-trailing-defaults.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error struct Heap; diff --git a/src/test/parse-fail/issue-17904.rs b/src/test/parse-fail/issue-17904.rs index 580b8c66c74..de5aeb02ab7 100644 --- a/src/test/parse-fail/issue-17904.rs +++ b/src/test/parse-fail/issue-17904.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error struct Baz<U> where U: Eq(U); //This is parsed as the new Fn* style parenthesis syntax. struct Baz<U> where U: Eq(U) -> R; // Notice this parses as well. diff --git a/src/test/parse-fail/lex-bad-octal-literal.rs b/src/test/parse-fail/lex-bad-octal-literal.rs index bf9880cb6cf..c8406af52ae 100644 --- a/src/test/parse-fail/lex-bad-octal-literal.rs +++ b/src/test/parse-fail/lex-bad-octal-literal.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// compile-flags: -Z parse-only -Z continue-parse-after-error + fn main() { 0o18; //~ ERROR invalid digit for a base 8 literal 0o1234_9_5670; //~ ERROR invalid digit for a base 8 literal diff --git a/src/test/parse-fail/lifetime-no-keyword.rs b/src/test/parse-fail/lifetime-no-keyword.rs index 9ca81d9918e..a8771ae93af 100644 --- a/src/test/parse-fail/lifetime-no-keyword.rs +++ b/src/test/parse-fail/lifetime-no-keyword.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error fn foo<'a>(a: &'a isize) { } fn bar(a: &'static isize) { } diff --git a/src/test/parse-fail/raw-byte-string-literals.rs b/src/test/parse-fail/raw-byte-string-literals.rs index d6be8fce53e..2e33f98add6 100644 --- a/src/test/parse-fail/raw-byte-string-literals.rs +++ b/src/test/parse-fail/raw-byte-string-literals.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error pub fn main() { - br"é"; //~ raw byte string must be ASCII - br##~"a"~##; //~ only `#` is allowed in raw string delimitation + br"é"; //~ ERROR raw byte string must be ASCII + br##~"a"~##; //~ ERROR only `#` is allowed in raw string delimitation } diff --git a/src/test/parse-fail/removed-syntax-field-let.rs b/src/test/parse-fail/removed-syntax-field-let.rs index 4e542fd7477..6deb3bb2e95 100644 --- a/src/test/parse-fail/removed-syntax-field-let.rs +++ b/src/test/parse-fail/removed-syntax-field-let.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error struct s { let foo: (), diff --git a/src/test/parse-fail/syntax-trait-polarity.rs b/src/test/parse-fail/syntax-trait-polarity.rs index c0d85034383..1971ffeaf26 100644 --- a/src/test/parse-fail/syntax-trait-polarity.rs +++ b/src/test/parse-fail/syntax-trait-polarity.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error #![feature(optin_builtin_traits)] diff --git a/src/test/parse-fail/trailing-plus-in-bounds.rs b/src/test/parse-fail/trailing-plus-in-bounds.rs index 4abdbad9a03..44bb1f930c7 100644 --- a/src/test/parse-fail/trailing-plus-in-bounds.rs +++ b/src/test/parse-fail/trailing-plus-in-bounds.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error use std::fmt::Debug; diff --git a/src/test/parse-fail/trait-bounds-not-on-impl.rs b/src/test/parse-fail/trait-bounds-not-on-impl.rs index 3bd8908d18b..b7dcc8a8b3b 100644 --- a/src/test/parse-fail/trait-bounds-not-on-impl.rs +++ b/src/test/parse-fail/trait-bounds-not-on-impl.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error trait Foo { } diff --git a/src/test/parse-fail/use-as-where-use-ends-with-mod-sep.rs b/src/test/parse-fail/use-as-where-use-ends-with-mod-sep.rs index c1e1cc1c7f7..9e16e29ba50 100644 --- a/src/test/parse-fail/use-as-where-use-ends-with-mod-sep.rs +++ b/src/test/parse-fail/use-as-where-use-ends-with-mod-sep.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error use std::any:: as foo; //~ ERROR expected identifier, found keyword `as` //~^ ERROR: expected one of `::`, `;`, or `as`, found `foo` diff --git a/src/test/parse-fail/where-clauses-no-bounds-or-predicates.rs b/src/test/parse-fail/where-clauses-no-bounds-or-predicates.rs index 3ac71176342..45165b76c4a 100644 --- a/src/test/parse-fail/where-clauses-no-bounds-or-predicates.rs +++ b/src/test/parse-fail/where-clauses-no-bounds-or-predicates.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: -Z parse-only +// compile-flags: -Z parse-only -Z continue-parse-after-error fn equal1<T>(_: &T, _: &T) -> bool where { //~^ ERROR a `where` clause must have at least one predicate in it diff --git a/src/test/run-pass/issue-18937-1.rs b/src/test/run-pass/issue-18937-1.rs new file mode 100644 index 00000000000..7a24d087b44 --- /dev/null +++ b/src/test/run-pass/issue-18937-1.rs @@ -0,0 +1,30 @@ +// 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. + +// Test that we are able to type-check this example. In particular, +// knowing that `T: 'a` allows us to deduce that `[U]: 'a` (because +// when `T=[U]` it implies that `U: 'a`). +// +// Regr. test for live code we found in the wild when fixing #18937. + +pub trait Leak<T : ?Sized> { + fn leak<'a>(self) -> &'a T where T: 'a; +} + +impl<U> Leak<[U]> for Vec<U> { + fn leak<'a>(mut self) -> &'a [U] where [U]: 'a { + let r: *mut [U] = &mut self[..]; + std::mem::forget(self); + unsafe { &mut *r } + } +} +fn main() { + println!("Hello, world!"); +} diff --git a/src/test/run-pass/traits-elaborate-type-region.rs b/src/test/run-pass/traits-elaborate-type-region.rs new file mode 100644 index 00000000000..4621c2ca4be --- /dev/null +++ b/src/test/run-pass/traits-elaborate-type-region.rs @@ -0,0 +1,58 @@ +// 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. + +#![allow(dead_code)] + +// Test that we elaborate `Type: 'region` constraints and infer various important things. + +trait Master<'a, T: ?Sized> { + fn foo() where T: 'a; +} + +// [U]: 'a => U: 'a +impl<'a, U> Master<'a, [U]> for () { + fn foo() where U: 'a { } +} + +// &'b U: 'a => 'b: 'a, U: 'a +impl<'a, 'b, U> Master<'a, &'b U> for () { + fn foo() where 'b: 'a, U: 'a { } +} + +// &'b [U]: 'a => 'b: 'a, U: 'a +impl<'a, 'b, U> Master<'a, &'b [U]> for () { + fn foo() where 'b: 'a, U: 'a { } +} + +// Foo<'b>: 'a => 'b: 'a +struct Foo<'a> { x: &'a () } +impl<'a, 'b> Master<'a, Foo<'b>> for () { + fn foo() where 'b: 'a { } +} + +// Bar<'b, T>: 'a => 'b: 'a, T: 'a +struct Bar<'a, T: 'a> { x: &'a T } +impl<'a, 'b, T> Master<'a, Bar<'b, T>> for () { + fn foo() where 'b: 'a, T: 'a { } +} + +// fn(T): 'a => T: 'a +impl<'a, T> Master<'a, fn(T)> for () { + fn foo() where T: 'a { } +} + +// fn() -> T: 'a => T: 'a +impl<'a, T> Master<'a, fn() -> T> for () { + fn foo() where T: 'a { } +} + +fn main() { + println!("Hello, world!"); +} diff --git a/src/test/ui/compare-method/proj-outlives-region.rs b/src/test/ui/compare-method/proj-outlives-region.rs new file mode 100644 index 00000000000..54cfe4be9c1 --- /dev/null +++ b/src/test/ui/compare-method/proj-outlives-region.rs @@ -0,0 +1,27 @@ +// 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. + +#![allow(dead_code)] +#![deny(extra_requirement_in_impl)] + +// Test that we elaborate `Type: 'region` constraints and infer various important things. + +trait Master<'a, T: ?Sized, U> { + fn foo() where T: 'a; +} + +// `U::Item: 'a` does not imply that `U: 'a` +impl<'a, U: Iterator> Master<'a, U::Item, U> for () { + fn foo() where U: 'a { } //~ ERROR E0276 +} + +fn main() { + println!("Hello, world!"); +} diff --git a/src/test/ui/compare-method/proj-outlives-region.stderr b/src/test/ui/compare-method/proj-outlives-region.stderr new file mode 100644 index 00000000000..79293e0deed --- /dev/null +++ b/src/test/ui/compare-method/proj-outlives-region.stderr @@ -0,0 +1,19 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/proj-outlives-region.rs:22:5 + | +17 | fn foo() where T: 'a; + | --------------------- definition of `foo` from trait +... +22 | fn foo() where U: 'a { } //~ ERROR E0276 + | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `U: 'a` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #37166 <https://github.com/rust-lang/rust/issues/37166> +note: lint level defined here + --> $DIR/proj-outlives-region.rs:12:9 + | +12 | #![deny(extra_requirement_in_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/compare-method/proj-outlives-region.stdout b/src/test/ui/compare-method/proj-outlives-region.stdout new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/test/ui/compare-method/proj-outlives-region.stdout diff --git a/src/test/compile-fail/region-bound-extra-bound-in-impl.rs b/src/test/ui/compare-method/region-extra-2.rs index 5bcc6be4c3d..b0cd3b8fdd2 100644 --- a/src/test/compile-fail/region-bound-extra-bound-in-impl.rs +++ b/src/test/ui/compare-method/region-extra-2.rs @@ -17,7 +17,7 @@ trait Tr<'a, T> { impl<'a, T> Tr<'a, T> for &'a mut [T] { fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { - //~^ ERROR lifetime bound not satisfied + //~^ ERROR E0276 &mut self[..] } } diff --git a/src/test/ui/compare-method/region-extra-2.stderr b/src/test/ui/compare-method/region-extra-2.stderr new file mode 100644 index 00000000000..54a551bcfed --- /dev/null +++ b/src/test/ui/compare-method/region-extra-2.stderr @@ -0,0 +1,11 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/region-extra-2.rs:19:5 + | +15 | fn renew<'b: 'a>(self) -> &'b mut [T]; + | -------------------------------------- definition of `renew` from trait +... +19 | fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { + | ^ impl has extra requirement `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/compare-method/region-extra.rs b/src/test/ui/compare-method/region-extra.rs new file mode 100644 index 00000000000..d61d0250211 --- /dev/null +++ b/src/test/ui/compare-method/region-extra.rs @@ -0,0 +1,27 @@ +// 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. + +#![allow(dead_code)] +#![deny(extra_requirement_in_impl)] + +// Test that you cannot add an extra where clause in the impl relating +// two regions. + +trait Master<'a, 'b> { + fn foo(); +} + +impl<'a, 'b> Master<'a, 'b> for () { + fn foo() where 'a: 'b { } +} + +fn main() { + println!("Hello, world!"); +} diff --git a/src/test/ui/compare-method/region-extra.stderr b/src/test/ui/compare-method/region-extra.stderr new file mode 100644 index 00000000000..e657813221a --- /dev/null +++ b/src/test/ui/compare-method/region-extra.stderr @@ -0,0 +1,11 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/region-extra.rs:22:5 + | +18 | fn foo(); + | --------- definition of `foo` from trait +... +22 | fn foo() where 'a: 'b { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/compare-method/region-extra.stdout b/src/test/ui/compare-method/region-extra.stdout new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/test/ui/compare-method/region-extra.stdout diff --git a/src/test/ui/compare-method/region-unrelated.rs b/src/test/ui/compare-method/region-unrelated.rs new file mode 100644 index 00000000000..8f79b30bd5f --- /dev/null +++ b/src/test/ui/compare-method/region-unrelated.rs @@ -0,0 +1,28 @@ +// 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. + +#![allow(dead_code)] +#![deny(extra_requirement_in_impl)] + +// Test that we elaborate `Type: 'region` constraints and infer various important things. + +trait Master<'a, T: ?Sized, U> { + fn foo() where T: 'a; +} + +// `U: 'a` does not imply `V: 'a` +impl<'a, U, V> Master<'a, U, V> for () { + fn foo() where V: 'a { } + //~^ ERROR parameter type `V` may not live long enough +} + +fn main() { + println!("Hello, world!"); +} diff --git a/src/test/ui/compare-method/region-unrelated.stderr b/src/test/ui/compare-method/region-unrelated.stderr new file mode 100644 index 00000000000..b7cfdf799bc --- /dev/null +++ b/src/test/ui/compare-method/region-unrelated.stderr @@ -0,0 +1,19 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/region-unrelated.rs:22:5 + | +17 | fn foo() where T: 'a; + | --------------------- definition of `foo` from trait +... +22 | fn foo() where V: 'a { } + | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `V: 'a` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #37166 <https://github.com/rust-lang/rust/issues/37166> +note: lint level defined here + --> $DIR/region-unrelated.rs:12:9 + | +12 | #![deny(extra_requirement_in_impl)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/compare-method/region-unrelated.stdout b/src/test/ui/compare-method/region-unrelated.stdout new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/test/ui/compare-method/region-unrelated.stdout diff --git a/src/test/compile-fail/issue-2611-5.rs b/src/test/ui/compare-method/reordered-type-param.rs index 440294f38ae..0b844d4521d 100644 --- a/src/test/compile-fail/issue-2611-5.rs +++ b/src/test/ui/compare-method/reordered-type-param.rs @@ -10,6 +10,8 @@ // Tests that ty params get matched correctly when comparing // an impl against a trait +// +// cc #26111 trait A { fn b<C:Clone,D>(&self, x: C) -> C; diff --git a/src/test/ui/compare-method/reordered-type-param.stderr b/src/test/ui/compare-method/reordered-type-param.stderr new file mode 100644 index 00000000000..985b85cc4ec --- /dev/null +++ b/src/test/ui/compare-method/reordered-type-param.stderr @@ -0,0 +1,14 @@ +error[E0053]: method `b` has an incompatible type for trait + --> $DIR/reordered-type-param.rs:26:30 + | +17 | fn b<C:Clone,D>(&self, x: C) -> C; + | - type in trait +... +26 | fn b<F:Clone,G>(&self, _x: G) -> G { panic!() } //~ ERROR method `b` has an incompatible type + | ^ expected type parameter, found a different type parameter + | + = note: expected type `fn(&E, F) -> F` + = note: found type `fn(&E, G) -> G` + +error: aborting due to previous error + diff --git a/src/test/compile-fail/issue-2611-4.rs b/src/test/ui/compare-method/trait-bound-on-type-parameter.rs index 16d7ea46846..09e9fb4ca2b 100644 --- a/src/test/compile-fail/issue-2611-4.rs +++ b/src/test/ui/compare-method/trait-bound-on-type-parameter.rs @@ -8,8 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Tests that an impl method's bounds aren't *more* restrictive -// than the trait method it's implementing +// Tests that impl can't add extra `F: Sync` bound aren't *more* restrictive +// than the trait method it's implementing. +// +// Regr test for #26111. trait A { fn b<C,D>(&self, x: C) -> C; @@ -20,8 +22,7 @@ struct E { } impl A for E { - fn b<F: Sync, G>(&self, _x: F) -> F { panic!() } - //~^ ERROR `F: std::marker::Sync` appears on the impl method + fn b<F: Sync, G>(&self, _x: F) -> F { panic!() } //~ ERROR E0276 } fn main() {} diff --git a/src/test/ui/compare-method/trait-bound-on-type-parameter.stderr b/src/test/ui/compare-method/trait-bound-on-type-parameter.stderr new file mode 100644 index 00000000000..7112a00c7b7 --- /dev/null +++ b/src/test/ui/compare-method/trait-bound-on-type-parameter.stderr @@ -0,0 +1,11 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/trait-bound-on-type-parameter.rs:25:5 + | +17 | fn b<C,D>(&self, x: C) -> C; + | ---------------------------- definition of `b` from trait +... +25 | fn b<F: Sync, G>(&self, _x: F) -> F { panic!() } //~ ERROR E0276 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `F: std::marker::Sync` + +error: aborting due to previous error + diff --git a/src/test/compile-fail/trait-bounds-impl-comparison-1.rs b/src/test/ui/compare-method/traits-misc-mismatch-1.rs index 9cf65a9d00d..cca282a1d19 100644 --- a/src/test/compile-fail/trait-bounds-impl-comparison-1.rs +++ b/src/test/ui/compare-method/traits-misc-mismatch-1.rs @@ -34,15 +34,15 @@ trait Foo { impl Foo for isize { // invalid bound for T, was defined as Eq in trait fn test_error1_fn<T: Ord>(&self) {} - //~^ ERROR the requirement `T: std::cmp::Ord` appears on the impl + //~^ ERROR E0276 // invalid bound for T, was defined as Eq + Ord in trait fn test_error2_fn<T: Eq + B>(&self) {} - //~^ ERROR the requirement `T: B` appears on the impl + //~^ ERROR E0276 // invalid bound for T, was defined as Eq + Ord in trait fn test_error3_fn<T: B + Eq>(&self) {} - //~^ ERROR the requirement `T: B` appears on the impl + //~^ ERROR E0276 // multiple bounds, same order as in trait fn test3_fn<T: Ord + Eq>(&self) {} @@ -52,16 +52,16 @@ impl Foo for isize { // parameters in impls must be equal or more general than in the defining trait fn test_error5_fn<T: B>(&self) {} - //~^ ERROR the requirement `T: B` appears on the impl + //~^ ERROR E0276 // bound `std::cmp::Eq` not enforced by this implementation, but this is OK fn test6_fn<T: A>(&self) {} fn test_error7_fn<T: A + Eq>(&self) {} - //~^ ERROR the requirement `T: std::cmp::Eq` appears on the impl + //~^ ERROR E0276 fn test_error8_fn<T: C>(&self) {} - //~^ ERROR the requirement `T: C` appears on the impl + //~^ ERROR E0276 } trait Getter<T> { @@ -74,7 +74,7 @@ trait Trait { impl Trait for usize { fn method<G: Getter<usize>>(&self) {} - //~^ ERROR `G: Getter<usize>` appears on the impl method + //~^ ERROR E0276 } fn main() {} diff --git a/src/test/ui/compare-method/traits-misc-mismatch-1.stderr b/src/test/ui/compare-method/traits-misc-mismatch-1.stderr new file mode 100644 index 00000000000..f221ebe3302 --- /dev/null +++ b/src/test/ui/compare-method/traits-misc-mismatch-1.stderr @@ -0,0 +1,65 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/traits-misc-mismatch-1.rs:36:5 + | +23 | fn test_error1_fn<T: Eq>(&self); + | -------------------------------- definition of `test_error1_fn` from trait +... +36 | fn test_error1_fn<T: Ord>(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::cmp::Ord` + +error[E0276]: impl has stricter requirements than trait + --> $DIR/traits-misc-mismatch-1.rs:40:5 + | +24 | fn test_error2_fn<T: Eq + Ord>(&self); + | -------------------------------------- definition of `test_error2_fn` from trait +... +40 | fn test_error2_fn<T: Eq + B>(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: B` + +error[E0276]: impl has stricter requirements than trait + --> $DIR/traits-misc-mismatch-1.rs:44:5 + | +25 | fn test_error3_fn<T: Eq + Ord>(&self); + | -------------------------------------- definition of `test_error3_fn` from trait +... +44 | fn test_error3_fn<T: B + Eq>(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: B` + +error[E0276]: impl has stricter requirements than trait + --> $DIR/traits-misc-mismatch-1.rs:54:5 + | +28 | fn test_error5_fn<T: A>(&self); + | ------------------------------- definition of `test_error5_fn` from trait +... +54 | fn test_error5_fn<T: B>(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: B` + +error[E0276]: impl has stricter requirements than trait + --> $DIR/traits-misc-mismatch-1.rs:60:5 + | +30 | fn test_error7_fn<T: A>(&self); + | ------------------------------- definition of `test_error7_fn` from trait +... +60 | fn test_error7_fn<T: A + Eq>(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::cmp::Eq` + +error[E0276]: impl has stricter requirements than trait + --> $DIR/traits-misc-mismatch-1.rs:63:5 + | +31 | fn test_error8_fn<T: B>(&self); + | ------------------------------- definition of `test_error8_fn` from trait +... +63 | fn test_error8_fn<T: C>(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: C` + +error[E0276]: impl has stricter requirements than trait + --> $DIR/traits-misc-mismatch-1.rs:76:5 + | +72 | fn method<G:Getter<isize>>(&self); + | ---------------------------------- definition of `method` from trait +... +76 | fn method<G: Getter<usize>>(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `G: Getter<usize>` + +error: aborting due to 7 previous errors + diff --git a/src/test/compile-fail/trait-bounds-impl-comparison-2.rs b/src/test/ui/compare-method/traits-misc-mismatch-2.rs index 8d587b29ba9..e82cf256df1 100644 --- a/src/test/compile-fail/trait-bounds-impl-comparison-2.rs +++ b/src/test/ui/compare-method/traits-misc-mismatch-2.rs @@ -21,7 +21,7 @@ trait IteratorUtil<A>: Sized impl<A, T: Iterator<A>> IteratorUtil<A> for T { fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<T, U> { - //~^ ERROR the requirement `U: Iterator<B>` appears on the impl method + //~^ ERROR E0276 ZipIterator{a: self, b: other} } } diff --git a/src/test/ui/compare-method/traits-misc-mismatch-2.stderr b/src/test/ui/compare-method/traits-misc-mismatch-2.stderr new file mode 100644 index 00000000000..5003550fd1e --- /dev/null +++ b/src/test/ui/compare-method/traits-misc-mismatch-2.stderr @@ -0,0 +1,11 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/traits-misc-mismatch-2.rs:23:5 + | +19 | fn zip<B, U: Iterator<U>>(self, other: U) -> ZipIterator<Self, U>; + | ------------------------------------------------------------------ definition of `zip` from trait +... +23 | fn zip<B, U: Iterator<B>>(self, other: U) -> ZipIterator<T, U> { + | ^ impl has extra requirement `U: Iterator<B>` + +error: aborting due to previous error + diff --git a/src/test/ui/update-references.sh b/src/test/ui/update-references.sh index f0a6f8a3d44..aa99d35f7aa 100755 --- a/src/test/ui/update-references.sh +++ b/src/test/ui/update-references.sh @@ -36,12 +36,12 @@ while [[ "$1" != "" ]]; do STDOUT_NAME="${1/%.rs/.stdout}" shift if [ -f $BUILD_DIR/$STDOUT_NAME ] && \ - ! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME > /dev/null); then + ! (diff $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME >& /dev/null); then echo updating $MYDIR/$STDOUT_NAME cp $BUILD_DIR/$STDOUT_NAME $MYDIR/$STDOUT_NAME fi if [ -f $BUILD_DIR/$STDERR_NAME ] && \ - ! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME > /dev/null); then + ! (diff $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME >& /dev/null); then echo updating $MYDIR/$STDERR_NAME cp $BUILD_DIR/$STDERR_NAME $MYDIR/$STDERR_NAME fi |
