From 1731bf8049258e63257f6dde48b375acafaeb4ef Mon Sep 17 00:00:00 2001 From: kennytm Date: Thu, 1 Mar 2018 05:16:44 +0800 Subject: [PATCH 1/6] Provide a proper span when demanding for the return type of `box x`. --- src/librustc_typeck/check/demand.rs | 8 ++++---- src/test/ui/did_you_mean/recursion_limit_deref.rs | 2 -- src/test/ui/did_you_mean/recursion_limit_deref.stderr | 10 +++------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 701b896b9057b..e8b953d40d7a1 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -16,7 +16,7 @@ use rustc::traits::ObligationCause; use syntax::ast; use syntax::util::parser::PREC_POSTFIX; -use syntax_pos::{self, Span}; +use syntax_pos::Span; use rustc::hir; use rustc::hir::def::Def; use rustc::hir::map::NodeItem; @@ -140,7 +140,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Some((msg, suggestion)) = self.check_ref(expr, checked_ty, expected) { err.span_suggestion(expr.span, msg, suggestion); } else if !self.check_for_cast(&mut err, expr, expr_ty, expected) { - let methods = self.get_conversion_methods(expected, checked_ty); + let methods = self.get_conversion_methods(expr.span, expected, checked_ty); if let Ok(expr_text) = self.tcx.sess.codemap().span_to_snippet(expr.span) { let suggestions = iter::repeat(expr_text).zip(methods.iter()) .map(|(receiver, method)| format!("{}.{}()", receiver, method.name)) @@ -155,9 +155,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { (expected, Some(err)) } - fn get_conversion_methods(&self, expected: Ty<'tcx>, checked_ty: Ty<'tcx>) + fn get_conversion_methods(&self, span: Span, expected: Ty<'tcx>, checked_ty: Ty<'tcx>) -> Vec { - let mut methods = self.probe_for_return_type(syntax_pos::DUMMY_SP, + let mut methods = self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.rs b/src/test/ui/did_you_mean/recursion_limit_deref.rs index 3e261ec636c08..f5e75f40fca08 100644 --- a/src/test/ui/did_you_mean/recursion_limit_deref.rs +++ b/src/test/ui/did_you_mean/recursion_limit_deref.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//~^^^^^^^^^^ ERROR reached the recursion limit - // Test that the recursion limit can be changed and that the compiler // suggests a fix. In this case, we have a long chain of Deref impls // which will cause an overflow during the autoderef loop. diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.stderr b/src/test/ui/did_you_mean/recursion_limit_deref.stderr index 2c8039615572c..20a94f7aac196 100644 --- a/src/test/ui/did_you_mean/recursion_limit_deref.stderr +++ b/src/test/ui/did_you_mean/recursion_limit_deref.stderr @@ -1,17 +1,13 @@ error[E0055]: reached the recursion limit while auto-dereferencing I - --> $DIR/recursion_limit_deref.rs:62:22 + --> $DIR/recursion_limit_deref.rs:60:22 | LL | let x: &Bottom = &t; //~ ERROR mismatched types | ^^ deref recursion limit reached | = help: consider adding a `#![recursion_limit="20"]` attribute to your crate -error[E0055]: reached the recursion limit while auto-dereferencing I - | - = help: consider adding a `#![recursion_limit="20"]` attribute to your crate - error[E0308]: mismatched types - --> $DIR/recursion_limit_deref.rs:62:22 + --> $DIR/recursion_limit_deref.rs:60:22 | LL | let x: &Bottom = &t; //~ ERROR mismatched types | ^^ expected struct `Bottom`, found struct `Top` @@ -19,7 +15,7 @@ LL | let x: &Bottom = &t; //~ ERROR mismatched types = note: expected type `&Bottom` found type `&Top` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors occurred: E0055, E0308. For more information about an error, try `rustc --explain E0055`. From abf4d8babfebcdad66599c50bf5b6ae3e5bcbeb5 Mon Sep 17 00:00:00 2001 From: kennytm Date: Mon, 26 Feb 2018 21:34:06 +0800 Subject: [PATCH 2/6] When picking a candidate, consider the unstable ones last. If there is potential ambiguity after stabilizing those candidates, a warning will be emitted. --- src/librustc/lint/builtin.rs | 9 +- src/librustc/middle/stability.rs | 72 +++++++---- src/librustc_lint/lib.rs | 10 +- src/librustc_typeck/check/method/probe.rs | 113 +++++++++++++++--- .../auxiliary/inference_unstable_iterator.rs | 24 ++++ .../auxiliary/inference_unstable_itertools.rs | 17 +++ src/test/ui/inference_unstable.rs | 29 +++++ src/test/ui/inference_unstable.stderr | 12 ++ src/test/ui/inference_unstable_featured.rs | 27 +++++ .../ui/inference_unstable_featured.stderr | 12 ++ src/test/ui/inference_unstable_forced.rs | 22 ++++ src/test/ui/inference_unstable_forced.stderr | 11 ++ 12 files changed, 317 insertions(+), 41 deletions(-) create mode 100644 src/test/ui/auxiliary/inference_unstable_iterator.rs create mode 100644 src/test/ui/auxiliary/inference_unstable_itertools.rs create mode 100644 src/test/ui/inference_unstable.rs create mode 100644 src/test/ui/inference_unstable.stderr create mode 100644 src/test/ui/inference_unstable_featured.rs create mode 100644 src/test/ui/inference_unstable_featured.stderr create mode 100644 src/test/ui/inference_unstable_forced.rs create mode 100644 src/test/ui/inference_unstable_forced.stderr diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index b4ed9c269bd88..97cfcf0f60795 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -260,6 +260,12 @@ declare_lint! { "floating-point literals cannot be used in patterns" } +declare_lint! { + pub UNSTABLE_NAME_COLLISION, + Warn, + "detects name collision with an existing but unstable method" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -307,7 +313,8 @@ impl LintPass for HardwiredLints { SINGLE_USE_LIFETIME, TYVAR_BEHIND_RAW_POINTER, ELIDED_LIFETIME_IN_PATH, - BARE_TRAIT_OBJECT + BARE_TRAIT_OBJECT, + UNSTABLE_NAME_COLLISION, ) } } diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 16c33d6bd837d..9193dc086fdbb 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -474,6 +474,22 @@ struct Checker<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, } +/// Result of `TyCtxt::eval_stability`. +pub enum EvalResult { + /// We can use the item because it is stable or we provided the + /// corresponding feature gate. + Allow, + /// We cannot use the item because it is unstable and we did not provide the + /// corresponding feature gate. + Deny { + feature: Symbol, + reason: Option, + issue: u32, + }, + /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. + Unmarked, +} + impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // (See issue #38412) fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool { @@ -509,11 +525,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - pub fn check_stability(self, def_id: DefId, id: NodeId, span: Span) { + /// Evaluates the stability of an item. + /// + /// Returns `None` if the item is stable, or unstable but the corresponding `#![feature]` has + /// been provided. Returns the tuple `Some((feature, reason, issue))` of the offending unstable + /// feature otherwise. + pub fn eval_stability(self, def_id: DefId, id: NodeId, span: Span) -> EvalResult { if span.allows_unstable() { debug!("stability: \ skipping span={:?} since it is internal", span); - return; + return EvalResult::Allow; } let lint_deprecated = |def_id: DefId, note: Option| { @@ -549,7 +570,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ..def_id }).is_some(); if !is_staged_api { - return; + return EvalResult::Allow; } let stability = self.lookup_stability(def_id); @@ -566,18 +587,18 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // Only the cross-crate scenario matters when checking unstable APIs let cross_crate = !def_id.is_local(); if !cross_crate { - return + return EvalResult::Allow; } // Issue 38412: private items lack stability markers. if self.skip_stability_check_due_to_privacy(def_id) { - return + return EvalResult::Allow; } match stability { - Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => { - if self.stability().active_features.contains(feature) { - return + Some(&Stability { level: attr::Unstable { reason, issue }, feature, .. }) => { + if self.stability().active_features.contains(&feature) { + return EvalResult::Allow; } // When we're compiling the compiler itself we may pull in @@ -589,19 +610,34 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // the `-Z force-unstable-if-unmarked` flag present (we're // compiling a compiler crate), then let this missing feature // annotation slide. - if *feature == "rustc_private" && issue == 27812 { + if feature == "rustc_private" && issue == 27812 { if self.sess.opts.debugging_opts.force_unstable_if_unmarked { - return + return EvalResult::Allow; } } - let msg = match *reason { - Some(ref r) => format!("use of unstable library feature '{}': {}", - feature.as_str(), &r), + EvalResult::Deny { feature, reason, issue } + } + Some(_) => { + // Stable APIs are always ok to call and deprecated APIs are + // handled by the lint emitting logic above. + EvalResult::Allow + } + None => { + EvalResult::Unmarked + } + } + } + + pub fn check_stability(self, def_id: DefId, id: NodeId, span: Span) { + match self.eval_stability(def_id, id, span) { + EvalResult::Allow => {} + EvalResult::Deny { feature, reason, issue } => { + let msg = match reason { + Some(r) => format!("use of unstable library feature '{}': {}", feature, r), None => format!("use of unstable library feature '{}'", &feature) }; - let msp: MultiSpan = span.into(); let cm = &self.sess.parse_sess.codemap(); let span_key = msp.primary_span().and_then(|sp: Span| @@ -624,12 +660,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { GateIssue::Library(Some(issue)), &msg); } } - Some(_) => { - // Stable APIs are always ok to call and deprecated APIs are - // handled by the lint emitting logic above. - } - None => { - span_bug!(span, "encountered unmarked API"); + EvalResult::Unmarked => { + span_bug!(span, "encountered unmarked API: {:?}", def_id); } } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 901d76edc8065..fad21ecef4d8e 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -273,7 +273,15 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(TYVAR_BEHIND_RAW_POINTER), reference: "issue #46906 ", edition: Some(Edition::Edition2018), - } + }, + FutureIncompatibleInfo { + id: LintId::of(UNSTABLE_NAME_COLLISION), + reference: "pr #48552 ", + edition: None, + // FIXME: create a proper tracking issue. + // Note: this item represents future incompatibility of all unstable functions in the + // standard library, and thus should never be removed or changed to an error. + }, ]); // Register renamed and removed lints diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index c7921d2bd4588..738e1ee87c72f 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -23,9 +23,10 @@ use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable}; use rustc::infer::type_variable::TypeVariableOrigin; use rustc::util::nodemap::FxHashSet; use rustc::infer::{self, InferOk}; +use rustc::middle::stability; use syntax::ast; use syntax::util::lev_distance::{lev_distance, find_best_match_for_name}; -use syntax_pos::Span; +use syntax_pos::{Span, symbol::Symbol}; use rustc::hir; use rustc::lint; use std::mem; @@ -937,30 +938,59 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); let mut possibly_unsatisfied_predicates = Vec::new(); - - debug!("searching inherent candidates"); - if let Some(pick) = self.consider_candidates(self_ty, - &self.inherent_candidates, - &mut possibly_unsatisfied_predicates) { - return Some(pick); + let mut unstable_candidates = Vec::new(); + + for (kind, candidates) in &[ + ("inherent", &self.inherent_candidates), + ("extension", &self.extension_candidates), + ] { + debug!("searching {} candidates", kind); + let res = self.consider_candidates( + self_ty, + candidates.iter(), + &mut possibly_unsatisfied_predicates, + Some(&mut unstable_candidates), + ); + if let Some(pick) = res { + if !unstable_candidates.is_empty() && !self_ty.is_ty_var() { + if let Ok(p) = &pick { + // Emit a lint if there are unstable candidates alongside the stable ones. + // + // Note, we suppress warning if `self_ty` is TyVar (`_`), since every + // possible candidates of every type will be considered, which leads to + // bogus ambiguity like `str::rsplit` vs `[_]::rsplit`. This condition is + // seen in `src/test/compile-fail/occurs-check-2.rs`. + self.emit_unstable_name_collision_hint(p, &unstable_candidates); + } + } + return Some(pick); + } } - debug!("searching extension candidates"); - let res = self.consider_candidates(self_ty, - &self.extension_candidates, - &mut possibly_unsatisfied_predicates); - if let None = res { + debug!("searching unstable candidates"); + let res = self.consider_candidates( + self_ty, + unstable_candidates.into_iter().map(|(c, _)| c), + &mut possibly_unsatisfied_predicates, + None, + ); + if res.is_none() { self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates); } res } - fn consider_candidates(&self, - self_ty: Ty<'tcx>, - probes: &[Candidate<'tcx>], - possibly_unsatisfied_predicates: &mut Vec>) - -> Option> { - let mut applicable_candidates: Vec<_> = probes.iter() + fn consider_candidates<'b, ProbesIter>( + &self, + self_ty: Ty<'tcx>, + probes: ProbesIter, + possibly_unsatisfied_predicates: &mut Vec>, + unstable_candidates: Option<&mut Vec<(&'b Candidate<'tcx>, Symbol)>>, + ) -> Option> + where + ProbesIter: Iterator> + Clone, + { + let mut applicable_candidates: Vec<_> = probes.clone() .map(|probe| { (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) }) @@ -975,8 +1005,20 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { } } + if let Some(uc) = unstable_candidates { + applicable_candidates.retain(|&(p, _)| { + if let stability::EvalResult::Deny { feature, .. } = + self.tcx.eval_stability(p.item.def_id, ast::DUMMY_NODE_ID, self.span) + { + uc.push((p, feature)); + return false; + } + true + }); + } + if applicable_candidates.len() > 1 { - let sources = probes.iter() + let sources = probes .map(|p| self.candidate_source(p, self_ty)) .collect(); return Some(Err(MethodError::Ambiguity(sources))); @@ -991,6 +1033,39 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { }) } + fn emit_unstable_name_collision_hint( + &self, + stable_pick: &Pick, + unstable_candidates: &[(&Candidate<'tcx>, Symbol)], + ) { + let mut diag = self.tcx.struct_span_lint_node( + lint::builtin::UNSTABLE_NAME_COLLISION, + self.fcx.body_id, + self.span, + "a method with this name will be added to the standard library in the future", + ); + + // FIXME: This should be a `span_suggestion` instead of `help`. However `self.span` only + // highlights the method name, so we can't use it. Also consider reusing the code from + // `report_method_error()`. + diag.help(&format!( + "call with fully qualified syntax `{}(...)` to keep using the current method", + self.tcx.item_path_str(stable_pick.item.def_id), + )); + + if ::rustc::session::config::nightly_options::is_nightly_build() { + for (candidate, feature) in unstable_candidates { + diag.note(&format!( + "add #![feature({})] to the crate attributes to enable `{}`", + feature, + self.tcx.item_path_str(candidate.item.def_id), + )); + } + } + + diag.emit(); + } + fn select_trait_candidate(&self, trait_ref: ty::TraitRef<'tcx>) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> { diff --git a/src/test/ui/auxiliary/inference_unstable_iterator.rs b/src/test/ui/auxiliary/inference_unstable_iterator.rs new file mode 100644 index 0000000000000..b73346e6332ca --- /dev/null +++ b/src/test/ui/auxiliary/inference_unstable_iterator.rs @@ -0,0 +1,24 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(staged_api)] + +#![stable(feature = "ipu_iterator", since = "1.0.0")] + +#[stable(feature = "ipu_iterator", since = "1.0.0")] +pub trait IpuIterator { + #[unstable(feature = "ipu_flatten", issue = "99999")] + fn ipu_flatten(&self) -> u32 { + 0 + } +} + +#[stable(feature = "ipu_iterator", since = "1.0.0")] +impl IpuIterator for char {} diff --git a/src/test/ui/auxiliary/inference_unstable_itertools.rs b/src/test/ui/auxiliary/inference_unstable_itertools.rs new file mode 100644 index 0000000000000..2ad264ee3d82f --- /dev/null +++ b/src/test/ui/auxiliary/inference_unstable_itertools.rs @@ -0,0 +1,17 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub trait IpuItertools { + fn ipu_flatten(&self) -> u32 { + 1 + } +} + +impl IpuItertools for char {} diff --git a/src/test/ui/inference_unstable.rs b/src/test/ui/inference_unstable.rs new file mode 100644 index 0000000000000..525fda33955a3 --- /dev/null +++ b/src/test/ui/inference_unstable.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Ensures #[unstable] functions without opting in the corresponding #![feature] +// will not break inference. + +// aux-build:inference_unstable_iterator.rs +// aux-build:inference_unstable_itertools.rs +// run-pass + +extern crate inference_unstable_iterator; +extern crate inference_unstable_itertools; + +#[allow(unused_imports)] +use inference_unstable_iterator::IpuIterator; +use inference_unstable_itertools::IpuItertools; + +fn main() { + assert_eq!('x'.ipu_flatten(), 1); + //~^ WARN a method with this name will be added to the standard library in the future + //~^^ WARN it will become a hard error in a future release +} diff --git a/src/test/ui/inference_unstable.stderr b/src/test/ui/inference_unstable.stderr new file mode 100644 index 0000000000000..a5cf4d6dff3b1 --- /dev/null +++ b/src/test/ui/inference_unstable.stderr @@ -0,0 +1,12 @@ +warning: a method with this name will be added to the standard library in the future + --> $DIR/inference_unstable.rs:26:20 + | +LL | assert_eq!('x'.ipu_flatten(), 1); + | ^^^^^^^^^^^ + | + = note: #[warn(unstable_name_collision)] on by default + = 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 pr #48552 + = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method + = note: add #![feature(ipu_flatten)] to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten` + diff --git a/src/test/ui/inference_unstable_featured.rs b/src/test/ui/inference_unstable_featured.rs new file mode 100644 index 0000000000000..f5c49bedc7117 --- /dev/null +++ b/src/test/ui/inference_unstable_featured.rs @@ -0,0 +1,27 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// There should be E0034 "multiple applicable items in scope" if we opt-in for +// the feature. + +// aux-build:inference_unstable_iterator.rs +// aux-build:inference_unstable_itertools.rs + +#![feature(ipu_flatten)] + +extern crate inference_unstable_iterator; +extern crate inference_unstable_itertools; + +use inference_unstable_iterator::IpuIterator; +use inference_unstable_itertools::IpuItertools; + +fn main() { + assert_eq!('x'.ipu_flatten(), 0); //~ ERROR E0034 +} diff --git a/src/test/ui/inference_unstable_featured.stderr b/src/test/ui/inference_unstable_featured.stderr new file mode 100644 index 0000000000000..cb5f3623291b5 --- /dev/null +++ b/src/test/ui/inference_unstable_featured.stderr @@ -0,0 +1,12 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/inference_unstable_featured.rs:26:20 + | +LL | assert_eq!('x'.ipu_flatten(), 0); //~ ERROR E0034 + | ^^^^^^^^^^^ multiple `ipu_flatten` found + | + = note: candidate #1 is defined in an impl of the trait `inference_unstable_iterator::IpuIterator` for the type `char` + = note: candidate #2 is defined in an impl of the trait `inference_unstable_itertools::IpuItertools` for the type `char` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/src/test/ui/inference_unstable_forced.rs b/src/test/ui/inference_unstable_forced.rs new file mode 100644 index 0000000000000..82ce4034ce269 --- /dev/null +++ b/src/test/ui/inference_unstable_forced.rs @@ -0,0 +1,22 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// If the unstable API is the only possible solution, +// still emit E0658 "use of unstable library feature". + +// aux-build:inference_unstable_iterator.rs + +extern crate inference_unstable_iterator; + +use inference_unstable_iterator::IpuIterator; + +fn main() { + assert_eq!('x'.ipu_flatten(), 0); //~ ERROR E0658 +} diff --git a/src/test/ui/inference_unstable_forced.stderr b/src/test/ui/inference_unstable_forced.stderr new file mode 100644 index 0000000000000..00eb81cd9a239 --- /dev/null +++ b/src/test/ui/inference_unstable_forced.stderr @@ -0,0 +1,11 @@ +error[E0658]: use of unstable library feature 'ipu_flatten' (see issue #99999) + --> $DIR/inference_unstable_forced.rs:21:20 + | +LL | assert_eq!('x'.ipu_flatten(), 0); //~ ERROR E0658 + | ^^^^^^^^^^^ + | + = help: add #![feature(ipu_flatten)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. From 023274483e5b61202c623352452cf17fcf455a6d Mon Sep 17 00:00:00 2001 From: kennytm Date: Sat, 3 Mar 2018 17:02:01 +0800 Subject: [PATCH 3/6] Changed `check_stability` to take an `Option` instead of `NodeId`. This clarifies the intent of whether to emit deprecated lint or not. --- src/librustc/middle/stability.rs | 56 +++++++++++++---------- src/librustc_typeck/astconv.rs | 4 +- src/librustc_typeck/check/_match.rs | 4 +- src/librustc_typeck/check/method/mod.rs | 4 +- src/librustc_typeck/check/method/probe.rs | 2 +- src/librustc_typeck/check/mod.rs | 6 +-- 6 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 9193dc086fdbb..29c8ac046b815 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -527,17 +527,21 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Evaluates the stability of an item. /// - /// Returns `None` if the item is stable, or unstable but the corresponding `#![feature]` has - /// been provided. Returns the tuple `Some((feature, reason, issue))` of the offending unstable - /// feature otherwise. - pub fn eval_stability(self, def_id: DefId, id: NodeId, span: Span) -> EvalResult { + /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding + /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending + /// unstable feature otherwise. + /// + /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been + /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to + /// `id`. + pub fn eval_stability(self, def_id: DefId, id: Option, span: Span) -> EvalResult { if span.allows_unstable() { debug!("stability: \ skipping span={:?} since it is internal", span); return EvalResult::Allow; } - let lint_deprecated = |def_id: DefId, note: Option| { + let lint_deprecated = |def_id: DefId, id: NodeId, note: Option| { let path = self.item_path_str(def_id); let msg = if let Some(note) = note { @@ -547,22 +551,21 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { }; self.lint_node(lint::builtin::DEPRECATED, id, span, &msg); + if id == ast::DUMMY_NODE_ID { + span_bug!(span, "emitted a deprecated lint with dummy node id: {:?}", def_id); + } }; // Deprecated attributes apply in-crate and cross-crate. - if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { - let skip = if id == ast::DUMMY_NODE_ID { - true - } else { + if let Some(id) = id { + if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { let parent_def_id = self.hir.local_def_id(self.hir.get_parent(id)); - self.lookup_deprecation_entry(parent_def_id).map_or(false, |parent_depr| { - parent_depr.same_origin(&depr_entry) - }) + let skip = self.lookup_deprecation_entry(parent_def_id) + .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); + if !skip { + lint_deprecated(def_id, id, depr_entry.attr.note); + } }; - - if !skip { - lint_deprecated(def_id, depr_entry.attr.note); - } } let is_staged_api = self.lookup_stability(DefId { @@ -579,8 +582,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { if let Some(&Stability{rustc_depr: Some(attr::RustcDeprecation { reason, .. }), ..}) = stability { - if id != ast::DUMMY_NODE_ID { - lint_deprecated(def_id, Some(reason)); + if let Some(id) = id { + lint_deprecated(def_id, id, Some(reason)); } } @@ -629,7 +632,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - pub fn check_stability(self, def_id: DefId, id: NodeId, span: Span) { + /// Checks if an item is stable or error out. + /// + /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not + /// exist, emits an error. + /// + /// Additionally, this function will also check if the item is deprecated. If so, and `id` is + /// not `None`, a deprecated lint attached to `id` will be emitted. + pub fn check_stability(self, def_id: DefId, id: Option, span: Span) { match self.eval_stability(def_id, id, span) { EvalResult::Allow => {} EvalResult::Deny { feature, reason, issue } => { @@ -687,7 +697,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { None => return, }; let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; - self.tcx.check_stability(def_id, item.id, item.span); + self.tcx.check_stability(def_id, Some(item.id), item.span); } // For implementations of traits, check the stability of each item @@ -700,8 +710,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { let trait_item_def_id = self.tcx.associated_items(trait_did) .find(|item| item.name == impl_item.name).map(|item| item.def_id); if let Some(def_id) = trait_item_def_id { - // Pass `DUMMY_NODE_ID` to skip deprecation warnings. - self.tcx.check_stability(def_id, ast::DUMMY_NODE_ID, impl_item.span); + // Pass `None` to skip deprecation warnings. + self.tcx.check_stability(def_id, None, impl_item.span); } } } @@ -737,7 +747,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { match path.def { Def::Local(..) | Def::Upvar(..) | Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => {} - _ => self.tcx.check_stability(path.def.def_id(), id, path.span) + _ => self.tcx.check_stability(path.def.def_id(), Some(id), path.span) } intravisit::walk_path(self, path) } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 827ca79334cbe..385154152b373 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -530,7 +530,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let msg = format!("associated type `{}` is private", binding.item_name); tcx.sess.span_err(binding.span, &msg); } - tcx.check_stability(assoc_ty.def_id, ref_id, binding.span); + tcx.check_stability(assoc_ty.def_id, Some(ref_id), binding.span); Ok(candidate.map_bound(|trait_ref| { ty::ProjectionPredicate { @@ -868,7 +868,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let msg = format!("{} `{}` is private", def.kind_name(), assoc_name); tcx.sess.span_err(span, &msg); } - tcx.check_stability(item.def_id, ref_id, span); + tcx.check_stability(item.def_id, Some(ref_id), span); (ty, def) } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 7965806af5d09..00c3b22780984 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -861,7 +861,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); self.check_pat_walk(&subpat, field_ty, def_bm, true); - self.tcx.check_stability(variant.fields[i].did, pat.id, subpat.span); + self.tcx.check_stability(variant.fields[i].did, Some(pat.id), subpat.span); } } else { let subpats_ending = if subpats.len() == 1 { "" } else { "s" }; @@ -923,7 +923,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); vacant.insert(span); field_map.get(&field.name) .map(|f| { - self.tcx.check_stability(f.did, pat_id, span); + self.tcx.check_stability(f.did, Some(pat_id), span); self.field_ty(span, f, substs) }) diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 1664f46464d15..54f41e65d06a0 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -171,7 +171,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .unwrap().insert(import_def_id); } - self.tcx.check_stability(pick.item.def_id, call_expr.id, span); + self.tcx.check_stability(pick.item.def_id, Some(call_expr.id), span); let result = self.confirm_method(span, self_expr, @@ -371,7 +371,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } let def = pick.item.def(); - self.tcx.check_stability(def.def_id(), expr_id, span); + self.tcx.check_stability(def.def_id(), Some(expr_id), span); Ok(def) } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 738e1ee87c72f..ede7703b61966 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -1008,7 +1008,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { if let Some(uc) = unstable_candidates { applicable_candidates.retain(|&(p, _)| { if let stability::EvalResult::Deny { feature, .. } = - self.tcx.eval_stability(p.item.def_id, ast::DUMMY_NODE_ID, self.span) + self.tcx.eval_stability(p.item.def_id, None, self.span) { uc.push((p, feature)); return false; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f86fe1fb75643..9e302c0ffb39a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3078,7 +3078,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.apply_adjustments(base, adjustments); autoderef.finalize(); - self.tcx.check_stability(field.did, expr.id, expr.span); + self.tcx.check_stability(field.did, Some(expr.id), expr.span); return field_ty; } @@ -3219,7 +3219,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) { let field_ty = self.field_ty(expr.span, field, substs); if field.vis.is_accessible_from(def_scope, self.tcx) { - self.tcx.check_stability(field.did, expr.id, expr.span); + self.tcx.check_stability(field.did, Some(expr.id), expr.span); Some(field_ty) } else { private_candidate = Some((base_def.did, field_ty)); @@ -3364,7 +3364,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // struct-like enums (yet...), but it's definitely not // a bug to have construct one. if adt_kind != ty::AdtKind::Enum { - tcx.check_stability(v_field.did, expr_id, field.span); + tcx.check_stability(v_field.did, Some(expr_id), field.span); } self.field_ty(field.span, v_field, substs) From 28b2bba58502d87a9a7d62649dccc946c1fc7e45 Mon Sep 17 00:00:00 2001 From: kennytm Date: Sat, 3 Mar 2018 19:27:49 +0800 Subject: [PATCH 4/6] Specialize future-incompatibility warning for UNSTABLE_NAME_COLLISION. --- src/librustc/lint/mod.rs | 20 +++++++++++++------- src/librustc_typeck/check/method/probe.rs | 2 +- src/test/ui/inference_unstable.rs | 4 ++-- src/test/ui/inference_unstable.stderr | 4 ++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index cd038d067a1fd..1497be2d5ba0d 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -498,15 +498,21 @@ pub fn struct_lint_level<'a>(sess: &'a Session, // Check for future incompatibility lints and issue a stronger warning. let lints = sess.lint_store.borrow(); - if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) { - let future = if let Some(edition) = future_incompatible.edition { - format!("the {} edition", edition) + let lint_id = LintId::of(lint); + if let Some(future_incompatible) = lints.future_incompatible(lint_id) { + const STANDARD_MESSAGE: &str = + "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error"; + + let explanation = if lint_id == LintId::of(::lint::builtin::UNSTABLE_NAME_COLLISION) { + "once this method is added to the standard library, \ + there will be ambiguity here, which will cause a hard error!" + .to_owned() + } else if let Some(edition) = future_incompatible.edition { + format!("{} in the {} edition!", STANDARD_MESSAGE, edition) } else { - "a future release".to_owned() + format!("{} in a future release!", STANDARD_MESSAGE) }; - let explanation = format!("this was previously accepted by the compiler \ - but is being phased out; \ - it will become a hard error in {}!", future); let citation = format!("for more information, see {}", future_incompatible.reference); err.warn(&explanation); diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index ede7703b61966..64d561ab96b09 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -1042,7 +1042,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { lint::builtin::UNSTABLE_NAME_COLLISION, self.fcx.body_id, self.span, - "a method with this name will be added to the standard library in the future", + "a method with this name may be added to the standard library in the future", ); // FIXME: This should be a `span_suggestion` instead of `help`. However `self.span` only diff --git a/src/test/ui/inference_unstable.rs b/src/test/ui/inference_unstable.rs index 525fda33955a3..816c443a06c21 100644 --- a/src/test/ui/inference_unstable.rs +++ b/src/test/ui/inference_unstable.rs @@ -24,6 +24,6 @@ use inference_unstable_itertools::IpuItertools; fn main() { assert_eq!('x'.ipu_flatten(), 1); - //~^ WARN a method with this name will be added to the standard library in the future - //~^^ WARN it will become a hard error in a future release + //~^ WARN a method with this name may be added to the standard library in the future + //~^^ WARN once this method is added to the standard library, there will be ambiguity here } diff --git a/src/test/ui/inference_unstable.stderr b/src/test/ui/inference_unstable.stderr index a5cf4d6dff3b1..21b1bdbf17910 100644 --- a/src/test/ui/inference_unstable.stderr +++ b/src/test/ui/inference_unstable.stderr @@ -1,11 +1,11 @@ -warning: a method with this name will be added to the standard library in the future +warning: a method with this name may be added to the standard library in the future --> $DIR/inference_unstable.rs:26:20 | LL | assert_eq!('x'.ipu_flatten(), 1); | ^^^^^^^^^^^ | = note: #[warn(unstable_name_collision)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = warning: once this method is added to the standard library, there will be ambiguity here, which will cause a hard error! = note: for more information, see pr #48552 = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method = note: add #![feature(ipu_flatten)] to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten` From db4f3f93bc3561fd0b125e45f693f16c9e1aa9bd Mon Sep 17 00:00:00 2001 From: kennytm Date: Sun, 11 Mar 2018 01:58:57 +0800 Subject: [PATCH 5/6] Filed a proper tracking issue. --- src/librustc_lint/lib.rs | 3 +-- src/test/ui/inference_unstable.stderr | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index fad21ecef4d8e..4639f7b2d28cd 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -276,9 +276,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { }, FutureIncompatibleInfo { id: LintId::of(UNSTABLE_NAME_COLLISION), - reference: "pr #48552 ", + reference: "issue #48919 ", edition: None, - // FIXME: create a proper tracking issue. // Note: this item represents future incompatibility of all unstable functions in the // standard library, and thus should never be removed or changed to an error. }, diff --git a/src/test/ui/inference_unstable.stderr b/src/test/ui/inference_unstable.stderr index 21b1bdbf17910..9c614d659d36f 100644 --- a/src/test/ui/inference_unstable.stderr +++ b/src/test/ui/inference_unstable.stderr @@ -6,7 +6,7 @@ LL | assert_eq!('x'.ipu_flatten(), 1); | = note: #[warn(unstable_name_collision)] on by default = warning: once this method is added to the standard library, there will be ambiguity here, which will cause a hard error! - = note: for more information, see pr #48552 + = note: for more information, see issue #48919 = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method = note: add #![feature(ipu_flatten)] to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten` From 17cc3d77d16e0cc3dfa1b3ee9749e2fca3ebb9e7 Mon Sep 17 00:00:00 2001 From: kennytm Date: Sat, 24 Mar 2018 02:32:20 +0800 Subject: [PATCH 6/6] Track IsSuggestion in ProbeContext. Don't warn stability for suggestions. --- src/librustc_typeck/check/method/probe.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 64d561ab96b09..136eb91e2abe6 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -39,6 +39,7 @@ pub use self::PickKind::*; /// Boolean flag used to indicate if this search is for a suggestion /// or not. If true, we can allow ambiguity and so forth. +#[derive(Clone, Copy)] pub struct IsSuggestion(pub bool); struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { @@ -66,6 +67,8 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used /// for error reporting unsatisfied_predicates: Vec>, + + is_suggestion: IsSuggestion, } impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> { @@ -277,8 +280,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // this creates one big transaction so that all type variables etc // that we create during the probe process are removed later self.probe(|_| { - let mut probe_cx = - ProbeContext::new(self, span, mode, method_name, return_type, Rc::new(steps)); + let mut probe_cx = ProbeContext::new( + self, span, mode, method_name, return_type, Rc::new(steps), is_suggestion, + ); probe_cx.assemble_inherent_candidates(); match scope { @@ -379,7 +383,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { mode: Mode, method_name: Option, return_type: Option>, - steps: Rc>>) + steps: Rc>>, + is_suggestion: IsSuggestion) -> ProbeContext<'a, 'gcx, 'tcx> { ProbeContext { fcx, @@ -395,6 +400,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { allow_similar_names: false, private_candidate: None, unsatisfied_predicates: Vec::new(), + is_suggestion, } } @@ -952,14 +958,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { Some(&mut unstable_candidates), ); if let Some(pick) = res { - if !unstable_candidates.is_empty() && !self_ty.is_ty_var() { + if !self.is_suggestion.0 && !unstable_candidates.is_empty() { if let Ok(p) = &pick { // Emit a lint if there are unstable candidates alongside the stable ones. // - // Note, we suppress warning if `self_ty` is TyVar (`_`), since every - // possible candidates of every type will be considered, which leads to - // bogus ambiguity like `str::rsplit` vs `[_]::rsplit`. This condition is - // seen in `src/test/compile-fail/occurs-check-2.rs`. + // We suppress warning if we're picking the method only because it is a + // suggestion. self.emit_unstable_name_collision_hint(p, &unstable_candidates); } } @@ -1265,7 +1269,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let steps = self.steps.clone(); self.probe(|_| { let mut pcx = ProbeContext::new(self.fcx, self.span, self.mode, self.method_name, - self.return_type, steps); + self.return_type, steps, IsSuggestion(true)); pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); pcx.assemble_extension_candidates_for_traits_in_scope(ast::DUMMY_NODE_ID)?;