diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index b4ed9c269bd88..97cfcf0f60795 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -260,6 +260,12 @@ declare_lint! { "floating-point literals cannot be used in patterns" } +declare_lint! { + pub UNSTABLE_NAME_COLLISION, + Warn, + "detects name collision with an existing but unstable method" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -307,7 +313,8 @@ impl LintPass for HardwiredLints { SINGLE_USE_LIFETIME, TYVAR_BEHIND_RAW_POINTER, ELIDED_LIFETIME_IN_PATH, - BARE_TRAIT_OBJECT + BARE_TRAIT_OBJECT, + UNSTABLE_NAME_COLLISION, ) } } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index cd038d067a1fd..1497be2d5ba0d 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -498,15 +498,21 @@ pub fn struct_lint_level<'a>(sess: &'a Session, // Check for future incompatibility lints and issue a stronger warning. let lints = sess.lint_store.borrow(); - if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) { - let future = if let Some(edition) = future_incompatible.edition { - format!("the {} edition", edition) + let lint_id = LintId::of(lint); + if let Some(future_incompatible) = lints.future_incompatible(lint_id) { + const STANDARD_MESSAGE: &str = + "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error"; + + let explanation = if lint_id == LintId::of(::lint::builtin::UNSTABLE_NAME_COLLISION) { + "once this method is added to the standard library, \ + there will be ambiguity here, which will cause a hard error!" + .to_owned() + } else if let Some(edition) = future_incompatible.edition { + format!("{} in the {} edition!", STANDARD_MESSAGE, edition) } else { - "a future release".to_owned() + format!("{} in a future release!", STANDARD_MESSAGE) }; - let explanation = format!("this was previously accepted by the compiler \ - but is being phased out; \ - it will become a hard error in {}!", future); let citation = format!("for more information, see {}", future_incompatible.reference); err.warn(&explanation); diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 16c33d6bd837d..29c8ac046b815 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -474,6 +474,22 @@ struct Checker<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, } +/// Result of `TyCtxt::eval_stability`. +pub enum EvalResult { + /// We can use the item because it is stable or we provided the + /// corresponding feature gate. + Allow, + /// We cannot use the item because it is unstable and we did not provide the + /// corresponding feature gate. + Deny { + feature: Symbol, + reason: Option, + issue: u32, + }, + /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. + Unmarked, +} + impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // (See issue #38412) fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool { @@ -509,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, 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| { + let lint_deprecated = |def_id: DefId, id: NodeId, note: Option| { let path = self.item_path_str(def_id); let msg = if let Some(note) = note { @@ -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, 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) } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 901d76edc8065..4639f7b2d28cd 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -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 ", edition: Some(Edition::Edition2018), - } + }, + FutureIncompatibleInfo { + id: LintId::of(UNSTABLE_NAME_COLLISION), + reference: "issue #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 diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 827ca79334cbe..385154152b373 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -530,7 +530,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let msg = format!("associated type `{}` is private", binding.item_name); tcx.sess.span_err(binding.span, &msg); } - tcx.check_stability(assoc_ty.def_id, ref_id, binding.span); + tcx.check_stability(assoc_ty.def_id, Some(ref_id), binding.span); Ok(candidate.map_bound(|trait_ref| { ty::ProjectionPredicate { @@ -868,7 +868,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let msg = format!("{} `{}` is private", def.kind_name(), assoc_name); tcx.sess.span_err(span, &msg); } - tcx.check_stability(item.def_id, ref_id, span); + tcx.check_stability(item.def_id, Some(ref_id), span); (ty, def) } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 7965806af5d09..00c3b22780984 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -861,7 +861,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); self.check_pat_walk(&subpat, field_ty, def_bm, true); - self.tcx.check_stability(variant.fields[i].did, pat.id, subpat.span); + self.tcx.check_stability(variant.fields[i].did, Some(pat.id), subpat.span); } } else { let subpats_ending = if subpats.len() == 1 { "" } else { "s" }; @@ -923,7 +923,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); vacant.insert(span); field_map.get(&field.name) .map(|f| { - self.tcx.check_stability(f.did, pat_id, span); + self.tcx.check_stability(f.did, Some(pat_id), span); self.field_ty(span, f, substs) }) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 701b896b9057b..e8b953d40d7a1 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -16,7 +16,7 @@ use rustc::traits::ObligationCause; use syntax::ast; use syntax::util::parser::PREC_POSTFIX; -use syntax_pos::{self, Span}; +use syntax_pos::Span; use rustc::hir; use rustc::hir::def::Def; use rustc::hir::map::NodeItem; @@ -140,7 +140,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Some((msg, suggestion)) = self.check_ref(expr, checked_ty, expected) { err.span_suggestion(expr.span, msg, suggestion); } else if !self.check_for_cast(&mut err, expr, expr_ty, expected) { - let methods = self.get_conversion_methods(expected, checked_ty); + let methods = self.get_conversion_methods(expr.span, expected, checked_ty); if let Ok(expr_text) = self.tcx.sess.codemap().span_to_snippet(expr.span) { let suggestions = iter::repeat(expr_text).zip(methods.iter()) .map(|(receiver, method)| format!("{}.{}()", receiver, method.name)) @@ -155,9 +155,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { (expected, Some(err)) } - fn get_conversion_methods(&self, expected: Ty<'tcx>, checked_ty: Ty<'tcx>) + fn get_conversion_methods(&self, span: Span, expected: Ty<'tcx>, checked_ty: Ty<'tcx>) -> Vec { - let mut methods = self.probe_for_return_type(syntax_pos::DUMMY_SP, + let mut methods = self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 1664f46464d15..54f41e65d06a0 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -171,7 +171,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .unwrap().insert(import_def_id); } - self.tcx.check_stability(pick.item.def_id, call_expr.id, span); + self.tcx.check_stability(pick.item.def_id, Some(call_expr.id), span); let result = self.confirm_method(span, self_expr, @@ -371,7 +371,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } let def = pick.item.def(); - self.tcx.check_stability(def.def_id(), expr_id, span); + self.tcx.check_stability(def.def_id(), Some(expr_id), span); Ok(def) } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index c7921d2bd4588..136eb91e2abe6 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -23,9 +23,10 @@ use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable}; use rustc::infer::type_variable::TypeVariableOrigin; use rustc::util::nodemap::FxHashSet; use rustc::infer::{self, InferOk}; +use rustc::middle::stability; use syntax::ast; use syntax::util::lev_distance::{lev_distance, find_best_match_for_name}; -use syntax_pos::Span; +use syntax_pos::{Span, symbol::Symbol}; use rustc::hir; use rustc::lint; use std::mem; @@ -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>, + + 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, return_type: Option>, - steps: Rc>>) + steps: Rc>>, + 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(); - - debug!("searching inherent candidates"); - if let Some(pick) = self.consider_candidates(self_ty, - &self.inherent_candidates, - &mut possibly_unsatisfied_predicates) { - return Some(pick); + let mut unstable_candidates = Vec::new(); + + for (kind, candidates) in &[ + ("inherent", &self.inherent_candidates), + ("extension", &self.extension_candidates), + ] { + debug!("searching {} candidates", kind); + let res = self.consider_candidates( + self_ty, + candidates.iter(), + &mut possibly_unsatisfied_predicates, + Some(&mut unstable_candidates), + ); + if let Some(pick) = res { + if !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>) - -> Option> { - let mut applicable_candidates: Vec<_> = probes.iter() + fn consider_candidates<'b, ProbesIter>( + &self, + self_ty: Ty<'tcx>, + probes: ProbesIter, + possibly_unsatisfied_predicates: &mut Vec>, + unstable_candidates: Option<&mut Vec<(&'b Candidate<'tcx>, Symbol)>>, + ) -> Option> + where + ProbesIter: Iterator> + Clone, + { + let mut applicable_candidates: Vec<_> = probes.clone() .map(|probe| { (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) }) @@ -975,8 +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)?; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f86fe1fb75643..9e302c0ffb39a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3078,7 +3078,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.apply_adjustments(base, adjustments); autoderef.finalize(); - self.tcx.check_stability(field.did, expr.id, expr.span); + self.tcx.check_stability(field.did, Some(expr.id), expr.span); return field_ty; } @@ -3219,7 +3219,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) { let field_ty = self.field_ty(expr.span, field, substs); if field.vis.is_accessible_from(def_scope, self.tcx) { - self.tcx.check_stability(field.did, expr.id, expr.span); + self.tcx.check_stability(field.did, Some(expr.id), expr.span); Some(field_ty) } else { private_candidate = Some((base_def.did, field_ty)); @@ -3364,7 +3364,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // struct-like enums (yet...), but it's definitely not // a bug to have construct one. if adt_kind != ty::AdtKind::Enum { - tcx.check_stability(v_field.did, expr_id, field.span); + tcx.check_stability(v_field.did, Some(expr_id), field.span); } self.field_ty(field.span, v_field, substs) diff --git a/src/test/ui/auxiliary/inference_unstable_iterator.rs b/src/test/ui/auxiliary/inference_unstable_iterator.rs new file mode 100644 index 0000000000000..b73346e6332ca --- /dev/null +++ b/src/test/ui/auxiliary/inference_unstable_iterator.rs @@ -0,0 +1,24 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(staged_api)] + +#![stable(feature = "ipu_iterator", since = "1.0.0")] + +#[stable(feature = "ipu_iterator", since = "1.0.0")] +pub trait IpuIterator { + #[unstable(feature = "ipu_flatten", issue = "99999")] + fn ipu_flatten(&self) -> u32 { + 0 + } +} + +#[stable(feature = "ipu_iterator", since = "1.0.0")] +impl IpuIterator for char {} diff --git a/src/test/ui/auxiliary/inference_unstable_itertools.rs b/src/test/ui/auxiliary/inference_unstable_itertools.rs new file mode 100644 index 0000000000000..2ad264ee3d82f --- /dev/null +++ b/src/test/ui/auxiliary/inference_unstable_itertools.rs @@ -0,0 +1,17 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub trait IpuItertools { + fn ipu_flatten(&self) -> u32 { + 1 + } +} + +impl IpuItertools for char {} diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.rs b/src/test/ui/did_you_mean/recursion_limit_deref.rs index 3e261ec636c08..f5e75f40fca08 100644 --- a/src/test/ui/did_you_mean/recursion_limit_deref.rs +++ b/src/test/ui/did_you_mean/recursion_limit_deref.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//~^^^^^^^^^^ ERROR reached the recursion limit - // Test that the recursion limit can be changed and that the compiler // suggests a fix. In this case, we have a long chain of Deref impls // which will cause an overflow during the autoderef loop. diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.stderr b/src/test/ui/did_you_mean/recursion_limit_deref.stderr index 2c8039615572c..20a94f7aac196 100644 --- a/src/test/ui/did_you_mean/recursion_limit_deref.stderr +++ b/src/test/ui/did_you_mean/recursion_limit_deref.stderr @@ -1,17 +1,13 @@ error[E0055]: reached the recursion limit while auto-dereferencing I - --> $DIR/recursion_limit_deref.rs:62:22 + --> $DIR/recursion_limit_deref.rs:60:22 | LL | let x: &Bottom = &t; //~ ERROR mismatched types | ^^ deref recursion limit reached | = help: consider adding a `#![recursion_limit="20"]` attribute to your crate -error[E0055]: reached the recursion limit while auto-dereferencing I - | - = help: consider adding a `#![recursion_limit="20"]` attribute to your crate - error[E0308]: mismatched types - --> $DIR/recursion_limit_deref.rs:62:22 + --> $DIR/recursion_limit_deref.rs:60:22 | LL | let x: &Bottom = &t; //~ ERROR mismatched types | ^^ expected struct `Bottom`, found struct `Top` @@ -19,7 +15,7 @@ LL | let x: &Bottom = &t; //~ ERROR mismatched types = note: expected type `&Bottom` found type `&Top` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors occurred: E0055, E0308. For more information about an error, try `rustc --explain E0055`. diff --git a/src/test/ui/inference_unstable.rs b/src/test/ui/inference_unstable.rs new file mode 100644 index 0000000000000..816c443a06c21 --- /dev/null +++ b/src/test/ui/inference_unstable.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Ensures #[unstable] functions without opting in the corresponding #![feature] +// will not break inference. + +// aux-build:inference_unstable_iterator.rs +// aux-build:inference_unstable_itertools.rs +// run-pass + +extern crate inference_unstable_iterator; +extern crate inference_unstable_itertools; + +#[allow(unused_imports)] +use inference_unstable_iterator::IpuIterator; +use inference_unstable_itertools::IpuItertools; + +fn main() { + assert_eq!('x'.ipu_flatten(), 1); + //~^ WARN a method with this name may be added to the standard library in the future + //~^^ WARN once this method is added to the standard library, there will be ambiguity here +} diff --git a/src/test/ui/inference_unstable.stderr b/src/test/ui/inference_unstable.stderr new file mode 100644 index 0000000000000..9c614d659d36f --- /dev/null +++ b/src/test/ui/inference_unstable.stderr @@ -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 + = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method + = note: add #![feature(ipu_flatten)] to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten` + diff --git a/src/test/ui/inference_unstable_featured.rs b/src/test/ui/inference_unstable_featured.rs new file mode 100644 index 0000000000000..f5c49bedc7117 --- /dev/null +++ b/src/test/ui/inference_unstable_featured.rs @@ -0,0 +1,27 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// There should be E0034 "multiple applicable items in scope" if we opt-in for +// the feature. + +// aux-build:inference_unstable_iterator.rs +// aux-build:inference_unstable_itertools.rs + +#![feature(ipu_flatten)] + +extern crate inference_unstable_iterator; +extern crate inference_unstable_itertools; + +use inference_unstable_iterator::IpuIterator; +use inference_unstable_itertools::IpuItertools; + +fn main() { + assert_eq!('x'.ipu_flatten(), 0); //~ ERROR E0034 +} diff --git a/src/test/ui/inference_unstable_featured.stderr b/src/test/ui/inference_unstable_featured.stderr new file mode 100644 index 0000000000000..cb5f3623291b5 --- /dev/null +++ b/src/test/ui/inference_unstable_featured.stderr @@ -0,0 +1,12 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/inference_unstable_featured.rs:26:20 + | +LL | assert_eq!('x'.ipu_flatten(), 0); //~ ERROR E0034 + | ^^^^^^^^^^^ multiple `ipu_flatten` found + | + = note: candidate #1 is defined in an impl of the trait `inference_unstable_iterator::IpuIterator` for the type `char` + = note: candidate #2 is defined in an impl of the trait `inference_unstable_itertools::IpuItertools` for the type `char` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/src/test/ui/inference_unstable_forced.rs b/src/test/ui/inference_unstable_forced.rs new file mode 100644 index 0000000000000..82ce4034ce269 --- /dev/null +++ b/src/test/ui/inference_unstable_forced.rs @@ -0,0 +1,22 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// If the unstable API is the only possible solution, +// still emit E0658 "use of unstable library feature". + +// aux-build:inference_unstable_iterator.rs + +extern crate inference_unstable_iterator; + +use inference_unstable_iterator::IpuIterator; + +fn main() { + assert_eq!('x'.ipu_flatten(), 0); //~ ERROR E0658 +} diff --git a/src/test/ui/inference_unstable_forced.stderr b/src/test/ui/inference_unstable_forced.stderr new file mode 100644 index 0000000000000..00eb81cd9a239 --- /dev/null +++ b/src/test/ui/inference_unstable_forced.stderr @@ -0,0 +1,11 @@ +error[E0658]: use of unstable library feature 'ipu_flatten' (see issue #99999) + --> $DIR/inference_unstable_forced.rs:21:20 + | +LL | assert_eq!('x'.ipu_flatten(), 0); //~ ERROR E0658 + | ^^^^^^^^^^^ + | + = help: add #![feature(ipu_flatten)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`.