When picking a candidate, consider the unstable ones last.

If there is potential ambiguity after stabilizing those candidates, a
warning will be emitted.
This commit is contained in:
kennytm 2018-02-26 21:34:06 +08:00
parent 1731bf8049
commit abf4d8babf
No known key found for this signature in database
GPG key ID: FEF6C8051D0E013C
12 changed files with 316 additions and 40 deletions

View file

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

View file

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

View file

@ -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 <https://github.com/rust-lang/rust/issues/46906>",
edition: Some(Edition::Edition2018),
}
},
FutureIncompatibleInfo {
id: LintId::of(UNSTABLE_NAME_COLLISION),
reference: "pr #48552 <https://github.com/rust-lang/rust/pull/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

View file

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

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

@ -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 will be added to the standard library in the future
//~^^ WARN it will become a hard error in a future release
}

View file

@ -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 <https://github.com/rust-lang/rust/pull/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`

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