Skip to content

Commit a0b0f5f

Browse files
committed
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.)
2 parents ab0ef14 + 17cc3d7 commit a0b0f5f

20 files changed

+381
-92
lines changed

src/librustc/lint/builtin.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,12 @@ declare_lint! {
260260
"floating-point literals cannot be used in patterns"
261261
}
262262

263+
declare_lint! {
264+
pub UNSTABLE_NAME_COLLISION,
265+
Warn,
266+
"detects name collision with an existing but unstable method"
267+
}
268+
263269
/// Does nothing as a lint pass, but registers some `Lint`s
264270
/// which are used by other parts of the compiler.
265271
#[derive(Copy, Clone)]
@@ -307,7 +313,8 @@ impl LintPass for HardwiredLints {
307313
SINGLE_USE_LIFETIME,
308314
TYVAR_BEHIND_RAW_POINTER,
309315
ELIDED_LIFETIME_IN_PATH,
310-
BARE_TRAIT_OBJECT
316+
BARE_TRAIT_OBJECT,
317+
UNSTABLE_NAME_COLLISION,
311318
)
312319
}
313320
}

src/librustc/lint/mod.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -498,15 +498,21 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
498498

499499
// Check for future incompatibility lints and issue a stronger warning.
500500
let lints = sess.lint_store.borrow();
501-
if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) {
502-
let future = if let Some(edition) = future_incompatible.edition {
503-
format!("the {} edition", edition)
501+
let lint_id = LintId::of(lint);
502+
if let Some(future_incompatible) = lints.future_incompatible(lint_id) {
503+
const STANDARD_MESSAGE: &str =
504+
"this was previously accepted by the compiler but is being phased out; \
505+
it will become a hard error";
506+
507+
let explanation = if lint_id == LintId::of(::lint::builtin::UNSTABLE_NAME_COLLISION) {
508+
"once this method is added to the standard library, \
509+
there will be ambiguity here, which will cause a hard error!"
510+
.to_owned()
511+
} else if let Some(edition) = future_incompatible.edition {
512+
format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
504513
} else {
505-
"a future release".to_owned()
514+
format!("{} in a future release!", STANDARD_MESSAGE)
506515
};
507-
let explanation = format!("this was previously accepted by the compiler \
508-
but is being phased out; \
509-
it will become a hard error in {}!", future);
510516
let citation = format!("for more information, see {}",
511517
future_incompatible.reference);
512518
err.warn(&explanation);

src/librustc/middle/stability.rs

+80-38
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,22 @@ struct Checker<'a, 'tcx: 'a> {
474474
tcx: TyCtxt<'a, 'tcx, 'tcx>,
475475
}
476476

477+
/// Result of `TyCtxt::eval_stability`.
478+
pub enum EvalResult {
479+
/// We can use the item because it is stable or we provided the
480+
/// corresponding feature gate.
481+
Allow,
482+
/// We cannot use the item because it is unstable and we did not provide the
483+
/// corresponding feature gate.
484+
Deny {
485+
feature: Symbol,
486+
reason: Option<Symbol>,
487+
issue: u32,
488+
},
489+
/// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
490+
Unmarked,
491+
}
492+
477493
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
478494
// (See issue #38412)
479495
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> {
509525
}
510526
}
511527

512-
pub fn check_stability(self, def_id: DefId, id: NodeId, span: Span) {
528+
/// Evaluates the stability of an item.
529+
///
530+
/// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
531+
/// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
532+
/// unstable feature otherwise.
533+
///
534+
/// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
535+
/// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
536+
/// `id`.
537+
pub fn eval_stability(self, def_id: DefId, id: Option<NodeId>, span: Span) -> EvalResult {
513538
if span.allows_unstable() {
514539
debug!("stability: \
515540
skipping span={:?} since it is internal", span);
516-
return;
541+
return EvalResult::Allow;
517542
}
518543

519-
let lint_deprecated = |def_id: DefId, note: Option<Symbol>| {
544+
let lint_deprecated = |def_id: DefId, id: NodeId, note: Option<Symbol>| {
520545
let path = self.item_path_str(def_id);
521546

522547
let msg = if let Some(note) = note {
@@ -526,30 +551,29 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
526551
};
527552

528553
self.lint_node(lint::builtin::DEPRECATED, id, span, &msg);
554+
if id == ast::DUMMY_NODE_ID {
555+
span_bug!(span, "emitted a deprecated lint with dummy node id: {:?}", def_id);
556+
}
529557
};
530558

531559
// Deprecated attributes apply in-crate and cross-crate.
532-
if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
533-
let skip = if id == ast::DUMMY_NODE_ID {
534-
true
535-
} else {
560+
if let Some(id) = id {
561+
if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
536562
let parent_def_id = self.hir.local_def_id(self.hir.get_parent(id));
537-
self.lookup_deprecation_entry(parent_def_id).map_or(false, |parent_depr| {
538-
parent_depr.same_origin(&depr_entry)
539-
})
563+
let skip = self.lookup_deprecation_entry(parent_def_id)
564+
.map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));
565+
if !skip {
566+
lint_deprecated(def_id, id, depr_entry.attr.note);
567+
}
540568
};
541-
542-
if !skip {
543-
lint_deprecated(def_id, depr_entry.attr.note);
544-
}
545569
}
546570

547571
let is_staged_api = self.lookup_stability(DefId {
548572
index: CRATE_DEF_INDEX,
549573
..def_id
550574
}).is_some();
551575
if !is_staged_api {
552-
return;
576+
return EvalResult::Allow;
553577
}
554578

555579
let stability = self.lookup_stability(def_id);
@@ -558,26 +582,26 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
558582

559583
if let Some(&Stability{rustc_depr: Some(attr::RustcDeprecation { reason, .. }), ..})
560584
= stability {
561-
if id != ast::DUMMY_NODE_ID {
562-
lint_deprecated(def_id, Some(reason));
585+
if let Some(id) = id {
586+
lint_deprecated(def_id, id, Some(reason));
563587
}
564588
}
565589

566590
// Only the cross-crate scenario matters when checking unstable APIs
567591
let cross_crate = !def_id.is_local();
568592
if !cross_crate {
569-
return
593+
return EvalResult::Allow;
570594
}
571595

572596
// Issue 38412: private items lack stability markers.
573597
if self.skip_stability_check_due_to_privacy(def_id) {
574-
return
598+
return EvalResult::Allow;
575599
}
576600

577601
match stability {
578-
Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => {
579-
if self.stability().active_features.contains(feature) {
580-
return
602+
Some(&Stability { level: attr::Unstable { reason, issue }, feature, .. }) => {
603+
if self.stability().active_features.contains(&feature) {
604+
return EvalResult::Allow;
581605
}
582606

583607
// When we're compiling the compiler itself we may pull in
@@ -589,19 +613,41 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
589613
// the `-Z force-unstable-if-unmarked` flag present (we're
590614
// compiling a compiler crate), then let this missing feature
591615
// annotation slide.
592-
if *feature == "rustc_private" && issue == 27812 {
616+
if feature == "rustc_private" && issue == 27812 {
593617
if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
594-
return
618+
return EvalResult::Allow;
595619
}
596620
}
597621

598-
let msg = match *reason {
599-
Some(ref r) => format!("use of unstable library feature '{}': {}",
600-
feature.as_str(), &r),
622+
EvalResult::Deny { feature, reason, issue }
623+
}
624+
Some(_) => {
625+
// Stable APIs are always ok to call and deprecated APIs are
626+
// handled by the lint emitting logic above.
627+
EvalResult::Allow
628+
}
629+
None => {
630+
EvalResult::Unmarked
631+
}
632+
}
633+
}
634+
635+
/// Checks if an item is stable or error out.
636+
///
637+
/// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
638+
/// exist, emits an error.
639+
///
640+
/// Additionally, this function will also check if the item is deprecated. If so, and `id` is
641+
/// not `None`, a deprecated lint attached to `id` will be emitted.
642+
pub fn check_stability(self, def_id: DefId, id: Option<NodeId>, span: Span) {
643+
match self.eval_stability(def_id, id, span) {
644+
EvalResult::Allow => {}
645+
EvalResult::Deny { feature, reason, issue } => {
646+
let msg = match reason {
647+
Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
601648
None => format!("use of unstable library feature '{}'", &feature)
602649
};
603650

604-
605651
let msp: MultiSpan = span.into();
606652
let cm = &self.sess.parse_sess.codemap();
607653
let span_key = msp.primary_span().and_then(|sp: Span|
@@ -624,12 +670,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
624670
GateIssue::Library(Some(issue)), &msg);
625671
}
626672
}
627-
Some(_) => {
628-
// Stable APIs are always ok to call and deprecated APIs are
629-
// handled by the lint emitting logic above.
630-
}
631-
None => {
632-
span_bug!(span, "encountered unmarked API");
673+
EvalResult::Unmarked => {
674+
span_bug!(span, "encountered unmarked API: {:?}", def_id);
633675
}
634676
}
635677
}
@@ -655,7 +697,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
655697
None => return,
656698
};
657699
let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
658-
self.tcx.check_stability(def_id, item.id, item.span);
700+
self.tcx.check_stability(def_id, Some(item.id), item.span);
659701
}
660702

661703
// For implementations of traits, check the stability of each item
@@ -668,8 +710,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
668710
let trait_item_def_id = self.tcx.associated_items(trait_did)
669711
.find(|item| item.name == impl_item.name).map(|item| item.def_id);
670712
if let Some(def_id) = trait_item_def_id {
671-
// Pass `DUMMY_NODE_ID` to skip deprecation warnings.
672-
self.tcx.check_stability(def_id, ast::DUMMY_NODE_ID, impl_item.span);
713+
// Pass `None` to skip deprecation warnings.
714+
self.tcx.check_stability(def_id, None, impl_item.span);
673715
}
674716
}
675717
}
@@ -705,7 +747,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
705747
match path.def {
706748
Def::Local(..) | Def::Upvar(..) |
707749
Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => {}
708-
_ => self.tcx.check_stability(path.def.def_id(), id, path.span)
750+
_ => self.tcx.check_stability(path.def.def_id(), Some(id), path.span)
709751
}
710752
intravisit::walk_path(self, path)
711753
}

src/librustc_lint/lib.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,14 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
273273
id: LintId::of(TYVAR_BEHIND_RAW_POINTER),
274274
reference: "issue #46906 <https://github.com/rust-lang/rust/issues/46906>",
275275
edition: Some(Edition::Edition2018),
276-
}
276+
},
277+
FutureIncompatibleInfo {
278+
id: LintId::of(UNSTABLE_NAME_COLLISION),
279+
reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>",
280+
edition: None,
281+
// Note: this item represents future incompatibility of all unstable functions in the
282+
// standard library, and thus should never be removed or changed to an error.
283+
},
277284
]);
278285

279286
// Register renamed and removed lints

src/librustc_typeck/astconv.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
530530
let msg = format!("associated type `{}` is private", binding.item_name);
531531
tcx.sess.span_err(binding.span, &msg);
532532
}
533-
tcx.check_stability(assoc_ty.def_id, ref_id, binding.span);
533+
tcx.check_stability(assoc_ty.def_id, Some(ref_id), binding.span);
534534

535535
Ok(candidate.map_bound(|trait_ref| {
536536
ty::ProjectionPredicate {
@@ -868,7 +868,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
868868
let msg = format!("{} `{}` is private", def.kind_name(), assoc_name);
869869
tcx.sess.span_err(span, &msg);
870870
}
871-
tcx.check_stability(item.def_id, ref_id, span);
871+
tcx.check_stability(item.def_id, Some(ref_id), span);
872872

873873
(ty, def)
874874
}

src/librustc_typeck/check/_match.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
861861
let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs);
862862
self.check_pat_walk(&subpat, field_ty, def_bm, true);
863863

864-
self.tcx.check_stability(variant.fields[i].did, pat.id, subpat.span);
864+
self.tcx.check_stability(variant.fields[i].did, Some(pat.id), subpat.span);
865865
}
866866
} else {
867867
let subpats_ending = if subpats.len() == 1 { "" } else { "s" };
@@ -923,7 +923,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
923923
vacant.insert(span);
924924
field_map.get(&field.name)
925925
.map(|f| {
926-
self.tcx.check_stability(f.did, pat_id, span);
926+
self.tcx.check_stability(f.did, Some(pat_id), span);
927927

928928
self.field_ty(span, f, substs)
929929
})

src/librustc_typeck/check/demand.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc::traits::ObligationCause;
1616

1717
use syntax::ast;
1818
use syntax::util::parser::PREC_POSTFIX;
19-
use syntax_pos::{self, Span};
19+
use syntax_pos::Span;
2020
use rustc::hir;
2121
use rustc::hir::def::Def;
2222
use rustc::hir::map::NodeItem;
@@ -140,7 +140,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
140140
if let Some((msg, suggestion)) = self.check_ref(expr, checked_ty, expected) {
141141
err.span_suggestion(expr.span, msg, suggestion);
142142
} else if !self.check_for_cast(&mut err, expr, expr_ty, expected) {
143-
let methods = self.get_conversion_methods(expected, checked_ty);
143+
let methods = self.get_conversion_methods(expr.span, expected, checked_ty);
144144
if let Ok(expr_text) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
145145
let suggestions = iter::repeat(expr_text).zip(methods.iter())
146146
.map(|(receiver, method)| format!("{}.{}()", receiver, method.name))
@@ -155,9 +155,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
155155
(expected, Some(err))
156156
}
157157

158-
fn get_conversion_methods(&self, expected: Ty<'tcx>, checked_ty: Ty<'tcx>)
158+
fn get_conversion_methods(&self, span: Span, expected: Ty<'tcx>, checked_ty: Ty<'tcx>)
159159
-> Vec<AssociatedItem> {
160-
let mut methods = self.probe_for_return_type(syntax_pos::DUMMY_SP,
160+
let mut methods = self.probe_for_return_type(span,
161161
probe::Mode::MethodCall,
162162
expected,
163163
checked_ty,

src/librustc_typeck/check/method/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
171171
.unwrap().insert(import_def_id);
172172
}
173173

174-
self.tcx.check_stability(pick.item.def_id, call_expr.id, span);
174+
self.tcx.check_stability(pick.item.def_id, Some(call_expr.id), span);
175175

176176
let result = self.confirm_method(span,
177177
self_expr,
@@ -371,7 +371,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
371371
}
372372

373373
let def = pick.item.def();
374-
self.tcx.check_stability(def.def_id(), expr_id, span);
374+
self.tcx.check_stability(def.def_id(), Some(expr_id), span);
375375

376376
Ok(def)
377377
}

0 commit comments

Comments
 (0)