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
This commit is contained in:
bors 2016-11-04 07:20:44 -07:00 committed by GitHub
commit ccfc38f034
59 changed files with 1717 additions and 789 deletions

View file

@ -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");
}
}
}
}

View file

@ -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(),
}
}
}

View file

@ -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
)
}
}

View file

@ -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);
}
}
}

View file

@ -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)]

View file

@ -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();

View file

@ -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",

View file

@ -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)),
}

View file

@ -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)]

View file

@ -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)

View file

@ -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,
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`.
//
// &'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
//
// '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)));
}
}
}

View file

@ -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

View file

@ -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()));
}

View file

@ -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);
}
}

View file

@ -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!();
}
}
}

View file

@ -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);

View file

@ -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

View file

@ -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,189 +41,57 @@ 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);
if let Err(ErrorReported) = compare_self_type(ccx,
impl_m,
impl_m_span,
trait_m) {
return;
}
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 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;
// 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
}
}
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();
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();
return;
}
// This code is best explained by example. Consider a trait:
//
// trait Trait<'t,T> {
@ -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;
}
// 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)?;
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);
// 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);
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);
// 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());
// 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;
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);
});
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;
// 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, &[]);
}
return true;
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>)
-> 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);
}
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),
};
return Ok(());
}
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 {
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.inputs.iter()
(&trait_m_sig.decl.output, 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))
}
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))
}
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),
};
}
_ => (origin.span(), tcx.map.span_if_local(trait_m.def_id)),
}
}
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))
}
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));
}
_ => (origin.span(), tcx.map.span_if_local(trait_m.def_id)),
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 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();
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),
}
} 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();
return Err(ErrorReported);
}
Ok(())
}
pub fn compare_const_impl<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,

View file

@ -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, \

View file

@ -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);
}

View file

@ -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, ...); }

View file

@ -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)>,

View file

@ -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
}
}

View file

@ -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);
}
}

View file

@ -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
}
}

View file

@ -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).

View file

@ -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;

View file

@ -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.

View file

@ -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

View file

@ -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) { }

View file

@ -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
}

View file

@ -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: (),

View file

@ -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)]

View file

@ -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;

View file

@ -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 {
}

View file

@ -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`

View file

@ -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

View file

@ -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!");
}

View file

@ -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!");
}

View file

@ -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!");
}

View file

@ -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

View file

@ -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[..]
}
}

View file

@ -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

View file

@ -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!");
}

View file

@ -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

View file

@ -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!");
}

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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() {}

View file

@ -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

View file

@ -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() {}

View file

@ -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

View file

@ -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}
}
}

View file

@ -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

View file

@ -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