1
Fork 0

Auto merge of #48552 - kennytm:lower-unstable-priority, r=nikomatsakis

Lower the priority of unstable methods when picking a candidate.

Previously, when searching for the impl of a method, we do not consider the stability of the impl. This leads to lots of insta-inference-regressions due to method ambiguity when a popular name is chosen. This has happened multiple times in Rust's history e.g.

* `f64::from_bits` #40470
* `Ord::{min, max}` #42496
* `Ord::clamp` #44095 (eventually got reverted due to these breakages)
* `Iterator::flatten` #48115 (recently added)

This PR changes the probing order so that unstable items are considered last. If a stable item is found, the unstable items will not be considered (but a future-incompatible warning will still be emitted), thus allowing stable code continue to function without using qualified names.

Once the unstable feature is stabilized, the ambiguity error will still be emitted, but the user can also use newly stable std methods, while the current situation is that downstream user is forced to update the code without any immediate benefit.

(I hope that we could bring back `Ord::clamp` if this PR is merged.)
This commit is contained in:
bors 2018-03-24 04:43:24 +00:00
commit a0b0f5fba5
20 changed files with 380 additions and 91 deletions

View file

@ -260,6 +260,12 @@ declare_lint! {
"floating-point literals cannot be used in patterns" "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 /// Does nothing as a lint pass, but registers some `Lint`s
/// which are used by other parts of the compiler. /// which are used by other parts of the compiler.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -307,7 +313,8 @@ impl LintPass for HardwiredLints {
SINGLE_USE_LIFETIME, SINGLE_USE_LIFETIME,
TYVAR_BEHIND_RAW_POINTER, TYVAR_BEHIND_RAW_POINTER,
ELIDED_LIFETIME_IN_PATH, ELIDED_LIFETIME_IN_PATH,
BARE_TRAIT_OBJECT BARE_TRAIT_OBJECT,
UNSTABLE_NAME_COLLISION,
) )
} }
} }

View file

@ -498,15 +498,21 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
// Check for future incompatibility lints and issue a stronger warning. // Check for future incompatibility lints and issue a stronger warning.
let lints = sess.lint_store.borrow(); let lints = sess.lint_store.borrow();
if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) { let lint_id = LintId::of(lint);
let future = if let Some(edition) = future_incompatible.edition { if let Some(future_incompatible) = lints.future_incompatible(lint_id) {
format!("the {} edition", edition) 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 { } 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 {}", let citation = format!("for more information, see {}",
future_incompatible.reference); future_incompatible.reference);
err.warn(&explanation); err.warn(&explanation);

View file

@ -474,6 +474,22 @@ struct Checker<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>, 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<Symbol>,
issue: u32,
},
/// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
Unmarked,
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
// (See issue #38412) // (See issue #38412)
fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool { fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool {
@ -509,14 +525,23 @@ 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 `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<NodeId>, span: Span) -> EvalResult {
if span.allows_unstable() { if span.allows_unstable() {
debug!("stability: \ debug!("stability: \
skipping span={:?} since it is internal", span); skipping span={:?} since it is internal", span);
return; return EvalResult::Allow;
} }
let lint_deprecated = |def_id: DefId, note: Option<Symbol>| { let lint_deprecated = |def_id: DefId, id: NodeId, note: Option<Symbol>| {
let path = self.item_path_str(def_id); let path = self.item_path_str(def_id);
let msg = if let Some(note) = note { let msg = if let Some(note) = note {
@ -526,22 +551,21 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}; };
self.lint_node(lint::builtin::DEPRECATED, id, span, &msg); 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. // Deprecated attributes apply in-crate and cross-crate.
if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { if let Some(id) = id {
let skip = if id == ast::DUMMY_NODE_ID { if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
true
} else {
let parent_def_id = self.hir.local_def_id(self.hir.get_parent(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| { let skip = self.lookup_deprecation_entry(parent_def_id)
parent_depr.same_origin(&depr_entry) .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 { let is_staged_api = self.lookup_stability(DefId {
@ -549,7 +573,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
..def_id ..def_id
}).is_some(); }).is_some();
if !is_staged_api { if !is_staged_api {
return; return EvalResult::Allow;
} }
let stability = self.lookup_stability(def_id); let stability = self.lookup_stability(def_id);
@ -558,26 +582,26 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
if let Some(&Stability{rustc_depr: Some(attr::RustcDeprecation { reason, .. }), ..}) if let Some(&Stability{rustc_depr: Some(attr::RustcDeprecation { reason, .. }), ..})
= stability { = stability {
if id != ast::DUMMY_NODE_ID { if let Some(id) = id {
lint_deprecated(def_id, Some(reason)); lint_deprecated(def_id, id, Some(reason));
} }
} }
// Only the cross-crate scenario matters when checking unstable APIs // Only the cross-crate scenario matters when checking unstable APIs
let cross_crate = !def_id.is_local(); let cross_crate = !def_id.is_local();
if !cross_crate { if !cross_crate {
return return EvalResult::Allow;
} }
// Issue 38412: private items lack stability markers. // Issue 38412: private items lack stability markers.
if self.skip_stability_check_due_to_privacy(def_id) { if self.skip_stability_check_due_to_privacy(def_id) {
return return EvalResult::Allow;
} }
match stability { match stability {
Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => { Some(&Stability { level: attr::Unstable { reason, issue }, feature, .. }) => {
if self.stability().active_features.contains(feature) { if self.stability().active_features.contains(&feature) {
return return EvalResult::Allow;
} }
// When we're compiling the compiler itself we may pull in // When we're compiling the compiler itself we may pull in
@ -589,19 +613,41 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
// the `-Z force-unstable-if-unmarked` flag present (we're // the `-Z force-unstable-if-unmarked` flag present (we're
// compiling a compiler crate), then let this missing feature // compiling a compiler crate), then let this missing feature
// annotation slide. // annotation slide.
if *feature == "rustc_private" && issue == 27812 { if feature == "rustc_private" && issue == 27812 {
if self.sess.opts.debugging_opts.force_unstable_if_unmarked { if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
return return EvalResult::Allow;
} }
} }
let msg = match *reason { EvalResult::Deny { feature, reason, issue }
Some(ref r) => format!("use of unstable library feature '{}': {}", }
feature.as_str(), &r), Some(_) => {
// Stable APIs are always ok to call and deprecated APIs are
// handled by the lint emitting logic above.
EvalResult::Allow
}
None => {
EvalResult::Unmarked
}
}
}
/// 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<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) None => format!("use of unstable library feature '{}'", &feature)
}; };
let msp: MultiSpan = span.into(); let msp: MultiSpan = span.into();
let cm = &self.sess.parse_sess.codemap(); let cm = &self.sess.parse_sess.codemap();
let span_key = msp.primary_span().and_then(|sp: Span| let span_key = msp.primary_span().and_then(|sp: Span|
@ -624,12 +670,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
GateIssue::Library(Some(issue)), &msg); GateIssue::Library(Some(issue)), &msg);
} }
} }
Some(_) => { EvalResult::Unmarked => {
// Stable APIs are always ok to call and deprecated APIs are span_bug!(span, "encountered unmarked API: {:?}", def_id);
// handled by the lint emitting logic above.
}
None => {
span_bug!(span, "encountered unmarked API");
} }
} }
} }
@ -655,7 +697,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
None => return, None => return,
}; };
let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; 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 // For implementations of traits, check the stability of each item
@ -668,8 +710,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
let trait_item_def_id = self.tcx.associated_items(trait_did) let trait_item_def_id = self.tcx.associated_items(trait_did)
.find(|item| item.name == impl_item.name).map(|item| item.def_id); .find(|item| item.name == impl_item.name).map(|item| item.def_id);
if let Some(def_id) = trait_item_def_id { if let Some(def_id) = trait_item_def_id {
// Pass `DUMMY_NODE_ID` to skip deprecation warnings. // Pass `None` to skip deprecation warnings.
self.tcx.check_stability(def_id, ast::DUMMY_NODE_ID, impl_item.span); self.tcx.check_stability(def_id, None, impl_item.span);
} }
} }
} }
@ -705,7 +747,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
match path.def { match path.def {
Def::Local(..) | Def::Upvar(..) | Def::Local(..) | Def::Upvar(..) |
Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => {} 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) intravisit::walk_path(self, path)
} }

View file

@ -273,7 +273,14 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
id: LintId::of(TYVAR_BEHIND_RAW_POINTER), id: LintId::of(TYVAR_BEHIND_RAW_POINTER),
reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>", reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>",
edition: Some(Edition::Edition2018), edition: Some(Edition::Edition2018),
} },
FutureIncompatibleInfo {
id: LintId::of(UNSTABLE_NAME_COLLISION),
reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>",
edition: None,
// 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 // Register renamed and removed lints

View file

@ -530,7 +530,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
let msg = format!("associated type `{}` is private", binding.item_name); let msg = format!("associated type `{}` is private", binding.item_name);
tcx.sess.span_err(binding.span, &msg); 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| { Ok(candidate.map_bound(|trait_ref| {
ty::ProjectionPredicate { 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); let msg = format!("{} `{}` is private", def.kind_name(), assoc_name);
tcx.sess.span_err(span, &msg); 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) (ty, def)
} }

View file

@ -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); let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs);
self.check_pat_walk(&subpat, field_ty, def_bm, true); 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 { } else {
let subpats_ending = if subpats.len() == 1 { "" } else { "s" }; 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); vacant.insert(span);
field_map.get(&field.name) field_map.get(&field.name)
.map(|f| { .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) self.field_ty(span, f, substs)
}) })

View file

@ -16,7 +16,7 @@ use rustc::traits::ObligationCause;
use syntax::ast; use syntax::ast;
use syntax::util::parser::PREC_POSTFIX; use syntax::util::parser::PREC_POSTFIX;
use syntax_pos::{self, Span}; use syntax_pos::Span;
use rustc::hir; use rustc::hir;
use rustc::hir::def::Def; use rustc::hir::def::Def;
use rustc::hir::map::NodeItem; 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) { if let Some((msg, suggestion)) = self.check_ref(expr, checked_ty, expected) {
err.span_suggestion(expr.span, msg, suggestion); err.span_suggestion(expr.span, msg, suggestion);
} else if !self.check_for_cast(&mut err, expr, expr_ty, expected) { } 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) { if let Ok(expr_text) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
let suggestions = iter::repeat(expr_text).zip(methods.iter()) let suggestions = iter::repeat(expr_text).zip(methods.iter())
.map(|(receiver, method)| format!("{}.{}()", receiver, method.name)) .map(|(receiver, method)| format!("{}.{}()", receiver, method.name))
@ -155,9 +155,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
(expected, Some(err)) (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<AssociatedItem> { -> Vec<AssociatedItem> {
let mut methods = self.probe_for_return_type(syntax_pos::DUMMY_SP, let mut methods = self.probe_for_return_type(span,
probe::Mode::MethodCall, probe::Mode::MethodCall,
expected, expected,
checked_ty, checked_ty,

View file

@ -171,7 +171,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
.unwrap().insert(import_def_id); .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, let result = self.confirm_method(span,
self_expr, self_expr,
@ -371,7 +371,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
let def = pick.item.def(); 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) Ok(def)
} }

View file

@ -23,9 +23,10 @@ use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
use rustc::infer::type_variable::TypeVariableOrigin; use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::util::nodemap::FxHashSet; use rustc::util::nodemap::FxHashSet;
use rustc::infer::{self, InferOk}; use rustc::infer::{self, InferOk};
use rustc::middle::stability;
use syntax::ast; use syntax::ast;
use syntax::util::lev_distance::{lev_distance, find_best_match_for_name}; 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::hir;
use rustc::lint; use rustc::lint;
use std::mem; use std::mem;
@ -38,6 +39,7 @@ pub use self::PickKind::*;
/// Boolean flag used to indicate if this search is for a suggestion /// Boolean flag used to indicate if this search is for a suggestion
/// or not. If true, we can allow ambiguity and so forth. /// or not. If true, we can allow ambiguity and so forth.
#[derive(Clone, Copy)]
pub struct IsSuggestion(pub bool); pub struct IsSuggestion(pub bool);
struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
@ -65,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 /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used
/// for error reporting /// for error reporting
unsatisfied_predicates: Vec<TraitRef<'tcx>>, unsatisfied_predicates: Vec<TraitRef<'tcx>>,
is_suggestion: IsSuggestion,
} }
impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> {
@ -276,8 +280,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// this creates one big transaction so that all type variables etc // this creates one big transaction so that all type variables etc
// that we create during the probe process are removed later // that we create during the probe process are removed later
self.probe(|_| { self.probe(|_| {
let mut probe_cx = let mut probe_cx = ProbeContext::new(
ProbeContext::new(self, span, mode, method_name, return_type, Rc::new(steps)); self, span, mode, method_name, return_type, Rc::new(steps), is_suggestion,
);
probe_cx.assemble_inherent_candidates(); probe_cx.assemble_inherent_candidates();
match scope { match scope {
@ -378,7 +383,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
mode: Mode, mode: Mode,
method_name: Option<ast::Name>, method_name: Option<ast::Name>,
return_type: Option<Ty<'tcx>>, return_type: Option<Ty<'tcx>>,
steps: Rc<Vec<CandidateStep<'tcx>>>) steps: Rc<Vec<CandidateStep<'tcx>>>,
is_suggestion: IsSuggestion)
-> ProbeContext<'a, 'gcx, 'tcx> { -> ProbeContext<'a, 'gcx, 'tcx> {
ProbeContext { ProbeContext {
fcx, fcx,
@ -394,6 +400,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
allow_similar_names: false, allow_similar_names: false,
private_candidate: None, private_candidate: None,
unsatisfied_predicates: Vec::new(), unsatisfied_predicates: Vec::new(),
is_suggestion,
} }
} }
@ -937,30 +944,57 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); debug!("pick_method(self_ty={})", self.ty_to_string(self_ty));
let mut possibly_unsatisfied_predicates = Vec::new(); let mut possibly_unsatisfied_predicates = Vec::new();
let mut unstable_candidates = Vec::new();
debug!("searching inherent candidates"); for (kind, candidates) in &[
if let Some(pick) = self.consider_candidates(self_ty, ("inherent", &self.inherent_candidates),
&self.inherent_candidates, ("extension", &self.extension_candidates),
&mut possibly_unsatisfied_predicates) { ] {
return Some(pick); 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 !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.
//
// We suppress warning if we're picking the method only because it is a
// suggestion.
self.emit_unstable_name_collision_hint(p, &unstable_candidates);
}
}
return Some(pick);
}
} }
debug!("searching extension candidates"); debug!("searching unstable candidates");
let res = self.consider_candidates(self_ty, let res = self.consider_candidates(
&self.extension_candidates, self_ty,
&mut possibly_unsatisfied_predicates); unstable_candidates.into_iter().map(|(c, _)| c),
if let None = res { &mut possibly_unsatisfied_predicates,
None,
);
if res.is_none() {
self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates); self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
} }
res res
} }
fn consider_candidates(&self, fn consider_candidates<'b, ProbesIter>(
self_ty: Ty<'tcx>, &self,
probes: &[Candidate<'tcx>], self_ty: Ty<'tcx>,
possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>) probes: ProbesIter,
-> Option<PickResult<'tcx>> { possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>,
let mut applicable_candidates: Vec<_> = probes.iter() unstable_candidates: Option<&mut Vec<(&'b Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>>
where
ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone,
{
let mut applicable_candidates: Vec<_> = probes.clone()
.map(|probe| { .map(|probe| {
(probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates))
}) })
@ -975,8 +1009,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, None, self.span)
{
uc.push((p, feature));
return false;
}
true
});
}
if applicable_candidates.len() > 1 { if applicable_candidates.len() > 1 {
let sources = probes.iter() let sources = probes
.map(|p| self.candidate_source(p, self_ty)) .map(|p| self.candidate_source(p, self_ty))
.collect(); .collect();
return Some(Err(MethodError::Ambiguity(sources))); return Some(Err(MethodError::Ambiguity(sources)));
@ -991,6 +1037,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 may 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>) fn select_trait_candidate(&self, trait_ref: ty::TraitRef<'tcx>)
-> traits::SelectionResult<'tcx, traits::Selection<'tcx>> -> traits::SelectionResult<'tcx, traits::Selection<'tcx>>
{ {
@ -1190,7 +1269,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
let steps = self.steps.clone(); let steps = self.steps.clone();
self.probe(|_| { self.probe(|_| {
let mut pcx = ProbeContext::new(self.fcx, self.span, self.mode, self.method_name, 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.allow_similar_names = true;
pcx.assemble_inherent_candidates(); pcx.assemble_inherent_candidates();
pcx.assemble_extension_candidates_for_traits_in_scope(ast::DUMMY_NODE_ID)?; pcx.assemble_extension_candidates_for_traits_in_scope(ast::DUMMY_NODE_ID)?;

View file

@ -3087,7 +3087,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.apply_adjustments(base, adjustments); self.apply_adjustments(base, adjustments);
autoderef.finalize(); 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; return field_ty;
} }
@ -3228,7 +3228,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) { if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) {
let field_ty = self.field_ty(expr.span, field, substs); let field_ty = self.field_ty(expr.span, field, substs);
if field.vis.is_accessible_from(def_scope, self.tcx) { 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) Some(field_ty)
} else { } else {
private_candidate = Some((base_def.did, field_ty)); private_candidate = Some((base_def.did, field_ty));
@ -3373,7 +3373,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// struct-like enums (yet...), but it's definitely not // struct-like enums (yet...), but it's definitely not
// a bug to have construct one. // a bug to have construct one.
if adt_kind != ty::AdtKind::Enum { 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) self.field_ty(field.span, v_field, substs)

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(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 {}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub trait IpuItertools {
fn ipu_flatten(&self) -> u32 {
1
}
}
impl IpuItertools for char {}

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
//~^^^^^^^^^^ ERROR reached the recursion limit
// Test that the recursion limit can be changed and that the compiler // 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 // suggests a fix. In this case, we have a long chain of Deref impls
// which will cause an overflow during the autoderef loop. // which will cause an overflow during the autoderef loop.

View file

@ -1,17 +1,13 @@
error[E0055]: reached the recursion limit while auto-dereferencing I 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 LL | let x: &Bottom = &t; //~ ERROR mismatched types
| ^^ deref recursion limit reached | ^^ deref recursion limit reached
| |
= help: consider adding a `#![recursion_limit="20"]` attribute to your crate = 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 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 LL | let x: &Bottom = &t; //~ ERROR mismatched types
| ^^ expected struct `Bottom`, found struct `Top` | ^^ expected struct `Bottom`, found struct `Top`
@ -19,7 +15,7 @@ LL | let x: &Bottom = &t; //~ ERROR mismatched types
= note: expected type `&Bottom` = note: expected type `&Bottom`
found type `&Top` found type `&Top`
error: aborting due to 3 previous errors error: aborting due to 2 previous errors
Some errors occurred: E0055, E0308. Some errors occurred: E0055, E0308.
For more information about an error, try `rustc --explain E0055`. For more information about an error, try `rustc --explain E0055`.

View file

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

View file

@ -0,0 +1,12 @@
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: 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 issue #48919 <https://github.com/rust-lang/rust/issues/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`

View file

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

View file

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

View file

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

View file

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