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:
commit
a0b0f5fba5
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<Symbol>,
|
||||
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,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() {
|
||||
debug!("stability: \
|
||||
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 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);
|
||||
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 {
|
||||
@ -549,7 +573,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);
|
||||
@ -558,26 +582,26 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
// 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 +613,41 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
};
|
||||
|
||||
|
||||
let msp: MultiSpan = span.into();
|
||||
let cm = &self.sess.parse_sess.codemap();
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -655,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
|
||||
@ -668,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -705,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)
|
||||
}
|
||||
|
@ -273,7 +273,14 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
|
||||
id: LintId::of(TYVAR_BEHIND_RAW_POINTER),
|
||||
reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>",
|
||||
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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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<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,
|
||||
expected,
|
||||
checked_ty,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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;
|
||||
@ -38,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> {
|
||||
@ -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
|
||||
/// for error reporting
|
||||
unsatisfied_predicates: Vec<TraitRef<'tcx>>,
|
||||
|
||||
is_suggestion: IsSuggestion,
|
||||
}
|
||||
|
||||
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
|
||||
// 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 {
|
||||
@ -378,7 +383,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||
mode: Mode,
|
||||
method_name: Option<ast::Name>,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
steps: Rc<Vec<CandidateStep<'tcx>>>)
|
||||
steps: Rc<Vec<CandidateStep<'tcx>>>,
|
||||
is_suggestion: IsSuggestion)
|
||||
-> ProbeContext<'a, 'gcx, 'tcx> {
|
||||
ProbeContext {
|
||||
fcx,
|
||||
@ -394,6 +400,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||
allow_similar_names: false,
|
||||
private_candidate: None,
|
||||
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));
|
||||
|
||||
let mut possibly_unsatisfied_predicates = Vec::new();
|
||||
let mut unstable_candidates = 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);
|
||||
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 !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");
|
||||
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<TraitRef<'tcx>>)
|
||||
-> Option<PickResult<'tcx>> {
|
||||
let mut applicable_candidates: Vec<_> = probes.iter()
|
||||
fn consider_candidates<'b, ProbesIter>(
|
||||
&self,
|
||||
self_ty: Ty<'tcx>,
|
||||
probes: ProbesIter,
|
||||
possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>,
|
||||
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| {
|
||||
(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 {
|
||||
let sources = probes.iter()
|
||||
let sources = probes
|
||||
.map(|p| self.candidate_source(p, self_ty))
|
||||
.collect();
|
||||
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>)
|
||||
-> traits::SelectionResult<'tcx, traits::Selection<'tcx>>
|
||||
{
|
||||
@ -1190,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)?;
|
||||
|
@ -3087,7 +3087,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;
|
||||
}
|
||||
@ -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) {
|
||||
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));
|
||||
@ -3373,7 +3373,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)
|
||||
|
24
src/test/ui/auxiliary/inference_unstable_iterator.rs
Normal file
24
src/test/ui/auxiliary/inference_unstable_iterator.rs
Normal 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 {}
|
17
src/test/ui/auxiliary/inference_unstable_itertools.rs
Normal file
17
src/test/ui/auxiliary/inference_unstable_itertools.rs
Normal 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 {}
|
@ -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.
|
||||
|
@ -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`.
|
||||
|
29
src/test/ui/inference_unstable.rs
Normal file
29
src/test/ui/inference_unstable.rs
Normal 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
|
||||
}
|
12
src/test/ui/inference_unstable.stderr
Normal file
12
src/test/ui/inference_unstable.stderr
Normal 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`
|
||||
|
27
src/test/ui/inference_unstable_featured.rs
Normal file
27
src/test/ui/inference_unstable_featured.rs
Normal 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
|
||||
}
|
12
src/test/ui/inference_unstable_featured.stderr
Normal file
12
src/test/ui/inference_unstable_featured.stderr
Normal 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`.
|
22
src/test/ui/inference_unstable_forced.rs
Normal file
22
src/test/ui/inference_unstable_forced.rs
Normal 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
|
||||
}
|
11
src/test/ui/inference_unstable_forced.stderr
Normal file
11
src/test/ui/inference_unstable_forced.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user