From 164f0105bb65f31b89e5fb7f368c9e6f5833a3f8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Oct 2016 22:19:19 +0200 Subject: Add safe_suggestion attribute --- src/libsyntax/feature_gate.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/libsyntax') diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 77c53542dcb..dbb31b0e56c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -304,6 +304,7 @@ declare_features! ( // Allows using `Self` and associated types in struct expressions and patterns. (active, more_struct_aliases, "1.14.0", Some(37544)), + // Allows #[link(..., cfg(..))] (active, link_cfg, "1.14.0", Some(37406)), @@ -314,6 +315,9 @@ declare_features! ( // Allows #[target_feature(...)] (active, target_feature, "1.15.0", None), + + // Allow safe suggestions for potential type conversions. + (active, safe_suggestion, "1.0.0", Some(37384)), ); declare_features! ( @@ -648,6 +652,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "internal implementation detail", cfg_fn!(rustc_attrs))), + ("safe_suggestion", Whitelisted, Gated("safe_suggestion", + "the `#[safe_suggestion]` attribute \ + is an experimental feature", + cfg_fn!(safe_suggestion))), + // FIXME: #14408 whitelist docs since rustdoc looks at them ("doc", Whitelisted, Ungated), -- cgit 1.4.1-3-g733a5 From b2d0ec0eb4cb468fefb66da7af67b9cf8132373e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 28 Oct 2016 01:04:16 +0200 Subject: Fix coercion ICE --- src/librustc_typeck/check/method/mod.rs | 21 ----------------- src/librustc_typeck/check/method/probe.rs | 39 +++++++++++++++++-------------- src/libsyntax/feature_gate.rs | 3 ++- 3 files changed, 24 insertions(+), 39 deletions(-) (limited to 'src/libsyntax') diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 334ef30487a..bd240c74133 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -354,25 +354,4 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { -> Option { self.tcx.associated_items(def_id).find(|item| item.name == item_name) } - - pub fn matches_return_type(&self, method: &ty::ImplOrTraitItem<'tcx>, - expected: ty::Ty<'tcx>) -> bool { - match *method { - ty::ImplOrTraitItem::MethodTraitItem(ref x) => { - self.can_sub_types(x.fty.sig.skip_binder().output, expected).is_ok() - } - _ => false, - } - } - - pub fn impl_or_return_item(&self, - def_id: DefId, - return_type: ty::Ty<'tcx>) - -> Option> { - self.tcx - .impl_or_trait_items(def_id) - .iter() - .map(|&did| self.tcx.impl_or_trait_item(did)) - .find(|m| self.matches_return_type(m, return_type)) - } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 85a2fcd2ba1..e735360274e 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -21,7 +21,8 @@ use rustc::ty::subst::{Subst, Substs}; use rustc::traits::{self, ObligationCause}; use rustc::ty::{self, Ty, ToPolyTraitRef, TraitRef, TypeFoldable}; use rustc::infer::type_variable::TypeVariableOrigin; -use rustc::util::nodemap::FxHashSet; +use rustc::util::nodemap::{FnvHashSet, FxHashSet}; +use rustc::infer::{self, InferOk, TypeOrigin}; use syntax::ast; use syntax_pos::Span; use rustc::hir; @@ -626,27 +627,27 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { Ok(()) } + pub fn matches_return_type(&self, method: &ty::ImplOrTraitItem<'tcx>, + expected: ty::Ty<'tcx>) -> bool { + match *method { + ty::ImplOrTraitItem::MethodTraitItem(ref x) => { + self.probe(|_| { + let output = self.replace_late_bound_regions_with_fresh_var( + self.span, infer::FnCall, &x.fty.sig.output()); + self.can_sub_types(output.0, expected).is_ok() + }) + } + _ => false, + } + } + fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: DefId) -> Result<(), MethodError<'tcx>> { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); - // Check whether `trait_def_id` defines a method with suitable name: - let trait_items = self.tcx.associated_items(trait_def_id); - let maybe_item = match self.looking_for { - LookingFor::MethodName(item_name) => { - trait_items.iter() - .find(|item| item.name == item_name) - } - LookingFor::ReturnType(item_ty) => { - trait_items.iter() - .find(|item| { - self.fcx.matches_return_type(item, &item_ty) - }) - } - }; - let item = match maybe_item { + let item = match self.impl_or_trait_item(trait_def_id) { Some(i) => i, None => { return Ok(()); @@ -1351,7 +1352,11 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.fcx.impl_or_trait_item(def_id, name) } LookingFor::ReturnType(return_ty) => { - self.fcx.impl_or_return_item(def_id, return_ty) + self.tcx + .impl_or_trait_items(def_id) + .iter() + .map(|&did| self.tcx.impl_or_trait_item(did)) + .find(|m| self.matches_return_type(m, return_ty)) } } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index dbb31b0e56c..a176a1ddede 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -652,7 +652,8 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "internal implementation detail", cfg_fn!(rustc_attrs))), - ("safe_suggestion", Whitelisted, Gated("safe_suggestion", + ("safe_suggestion", Whitelisted, Gated(Stability::Unstable, + "safe_suggestion", "the `#[safe_suggestion]` attribute \ is an experimental feature", cfg_fn!(safe_suggestion))), -- cgit 1.4.1-3-g733a5 From c11fe553df269d6f47b4c48f5c47c08efdd373dc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 28 Oct 2016 14:27:42 +0200 Subject: Create check_ref method to allow to check coercion with & types --- src/libcollections/lib.rs | 1 - src/libcollections/string.rs | 1 - src/librustc/infer/error_reporting.rs | 2 +- src/librustc_typeck/check/demand.rs | 116 +++++++++++++++------------- src/librustc_typeck/check/method/probe.rs | 16 ++-- src/libsyntax/feature_gate.rs | 6 -- src/test/compile-fail/coerce_suggestions.rs | 41 ++++++++++ 7 files changed, 114 insertions(+), 69 deletions(-) create mode 100644 src/test/compile-fail/coerce_suggestions.rs (limited to 'src/libsyntax') diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 673a717b5f8..68b067012d3 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -28,7 +28,6 @@ #![cfg_attr(test, allow(deprecated))] // rand #![cfg_attr(not(stage0), deny(warnings))] -#![cfg_attr(not(stage0), feature(safe_suggestion))] #![feature(alloc)] #![feature(allow_internal_unstable)] diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index a5017d5d701..b4c41a99a6b 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -1231,7 +1231,6 @@ impl String { /// assert_eq!(a.len(), 3); /// ``` #[inline] - #[cfg_attr(not(stage0), safe_suggestion)] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { self.vec.len() diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index a67af1d5dcc..90d752ae6ee 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -549,7 +549,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { let expected_found = match values { None => None, - Some(ref values) => match self.values_str(&values) { + Some(values) => match self.values_str(&values) { Some((expected, found)) => Some((expected, found)), None => { // Derived error. Cancel the emitter. diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 01a961949bc..6246e95faeb 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -19,28 +19,10 @@ use syntax_pos::{self, Span}; use rustc::hir; use rustc::ty::{self, ImplOrTraitItem}; -use hir::def_id::DefId; - use std::rc::Rc; use super::method::probe; -struct MethodInfo<'tcx> { - ast: Option, - id: DefId, - item: Rc>, -} - -impl<'tcx> MethodInfo<'tcx> { - fn new(ast: Option, id: DefId, item: Rc>) -> MethodInfo { - MethodInfo { - ast: ast, - id: id, - item: item, - } - } -} - impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Requires that the two types unify, and prints an error message if // they don't. @@ -79,6 +61,46 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } + fn check_ref(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, + expected: Ty<'tcx>) -> Option { + match (&checked_ty.sty, &expected.sty) { + (&ty::TyRef(_, x_mutability), &ty::TyRef(_, y_mutability)) => { + // check if there is a mutability difference + if x_mutability.mutbl == hir::Mutability::MutImmutable && + x_mutability.mutbl != y_mutability.mutbl && + self.can_sub_types(&x_mutability.ty, y_mutability.ty).is_ok() { + if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) { + return Some(format!("try with `&mut {}`", &src.replace("&", ""))); + } + } + None + } + (_, &ty::TyRef(_, mutability)) => { + // check if it can work when put into a ref + let ref_ty = match mutability.mutbl { + hir::Mutability::MutMutable => self.tcx.mk_mut_ref( + self.tcx.mk_region(ty::ReStatic), + checked_ty), + hir::Mutability::MutImmutable => self.tcx.mk_imm_ref( + self.tcx.mk_region(ty::ReStatic), + checked_ty), + }; + if self.try_coerce(expr, ref_ty, expected).is_ok() { + if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) { + return Some(format!("try with `{}{}`", + match mutability.mutbl { + hir::Mutability::MutMutable => "&mut ", + hir::Mutability::MutImmutable => "&", + }, + &src)); + } + } + None + } + _ => None, + } + } + // Checks that the type of `expr` can be coerced to `expected`. pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) { let expected = self.resolve_type_vars_with_obligations(expected); @@ -86,34 +108,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); let mode = probe::Mode::MethodCall; - let suggestions = - if let Ok(methods) = self.probe_return(syntax_pos::DUMMY_SP, mode, expected, - checked_ty, ast::DUMMY_NODE_ID) { + let suggestions = if let Some(s) = self.check_ref(expr, checked_ty, expected) { + Some(s) + } else if let Ok(methods) = self.probe_return(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID) { let suggestions: Vec<_> = methods.iter() - .filter_map(|ref x| { - if let Some(id) = self.get_impl_id(&x.item) { - Some(MethodInfo::new(None, id, Rc::new(x.item.clone()))) - } else { - None - }}) + .map(|ref x| { + Rc::new(x.item.clone()) + }) .collect(); if suggestions.len() > 0 { - let safe_suggestions: Vec<_> = - suggestions.iter() - .map(|ref x| MethodInfo::new( - self.find_attr(x.id, "safe_suggestion"), - x.id, - x.item.clone())) - .filter(|ref x| x.ast.is_some()) - .collect(); - Some(if safe_suggestions.len() > 0 { - self.get_best_match(&safe_suggestions) - } else { - format!("no safe suggestion found, here are functions which match your \ - needs but be careful:\n - {}", - self.get_best_match(&suggestions)) - }) + Some(format!("here are some functions which \ + might fulfill your needs:\n - {}", + self.get_best_match(&suggestions))) } else { None } @@ -132,34 +143,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - fn get_best_match(&self, methods: &[MethodInfo<'tcx>]) -> String { + fn get_best_match(&self, methods: &[Rc>]) -> String { if methods.len() == 1 { - return format!(" - {}", methods[0].item.name()); + return format!(" - {}", methods[0].name()); } - let no_argument_methods: Vec<&MethodInfo> = + let no_argument_methods: Vec<&Rc>> = methods.iter() - .filter(|ref x| self.has_not_input_arg(&*x.item)) + .filter(|ref x| self.has_not_input_arg(&*x)) .collect(); if no_argument_methods.len() > 0 { no_argument_methods.iter() - .map(|method| format!("{}", method.item.name())) + .take(5) + .map(|method| format!("{}", method.name())) .collect::>() .join("\n - ") } else { methods.iter() - .map(|method| format!("{}", method.item.name())) + .take(5) + .map(|method| format!("{}", method.name())) .collect::>() .join("\n - ") } } - fn get_impl_id(&self, impl_: &ImplOrTraitItem<'tcx>) -> Option { - match *impl_ { - ty::ImplOrTraitItem::MethodTraitItem(ref m) => Some((*m).def_id), - _ => None, - } - } - fn has_not_input_arg(&self, method: &ImplOrTraitItem<'tcx>) -> bool { match *method { ImplOrTraitItem::MethodTraitItem(ref x) => { diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index e735360274e..4a63c967920 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -194,7 +194,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // think cause spurious errors. Really though this part should // take place in the `self.probe` below. let steps = if mode == Mode::MethodCall { - match self.create_steps(span, self_ty) { + match self.create_steps(span, self_ty, &looking_for) { Some(steps) => steps, None => { return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(), @@ -247,7 +247,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { fn create_steps(&self, span: Span, - self_ty: Ty<'tcx>) + self_ty: Ty<'tcx>, + looking_for: &LookingFor<'tcx>) -> Option>> { // FIXME: we don't need to create the entire steps in one pass @@ -262,7 +263,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }) .collect(); - let final_ty = autoderef.unambiguous_final_ty(); + let final_ty = match looking_for { + &LookingFor::MethodName(_) => autoderef.unambiguous_final_ty(), + &LookingFor::ReturnType(_) => self_ty, + }; match final_ty.sty { ty::TyArray(elem_ty, _) => { let dereferences = steps.len() - 1; @@ -628,13 +632,15 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { } pub fn matches_return_type(&self, method: &ty::ImplOrTraitItem<'tcx>, - expected: ty::Ty<'tcx>) -> bool { + expected: ty::Ty<'tcx>) -> bool { match *method { ty::ImplOrTraitItem::MethodTraitItem(ref x) => { self.probe(|_| { let output = self.replace_late_bound_regions_with_fresh_var( self.span, infer::FnCall, &x.fty.sig.output()); - self.can_sub_types(output.0, expected).is_ok() + let substs = self.fresh_substs_for_item(self.span, method.def_id()); + let output = output.0.subst(self.tcx, substs); + self.can_sub_types(output, expected).is_ok() }) } _ => false, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index a176a1ddede..e04cc11f15e 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -652,12 +652,6 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "internal implementation detail", cfg_fn!(rustc_attrs))), - ("safe_suggestion", Whitelisted, Gated(Stability::Unstable, - "safe_suggestion", - "the `#[safe_suggestion]` attribute \ - is an experimental feature", - cfg_fn!(safe_suggestion))), - // FIXME: #14408 whitelist docs since rustdoc looks at them ("doc", Whitelisted, Ungated), diff --git a/src/test/compile-fail/coerce_suggestions.rs b/src/test/compile-fail/coerce_suggestions.rs new file mode 100644 index 00000000000..decd589e6f4 --- /dev/null +++ b/src/test/compile-fail/coerce_suggestions.rs @@ -0,0 +1,41 @@ +// Copyright 2016 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. + +fn test(_x: &mut String) {} +fn test2(_x: &mut i32) {} + +fn main() { + let x: usize = String::new(); + //^ ERROR E0308 + //| NOTE expected type `usize` + //| NOTE found type `std::string::String` + //| NOTE here are some functions which might fulfill your needs: + let x: &str = String::new(); + //^ ERROR E0308 + //| NOTE expected type `&str` + //| NOTE found type `std::string::String` + //| NOTE try with `&String::new()` + let y = String::new(); + test(&y); + //^ ERROR E0308 + //| NOTE expected type `&mut std::string::String` + //| NOTE found type `&std::string::String` + //| NOTE try with `&mut y` + test2(&y); + //^ ERROR E0308 + //| NOTE expected type `&mut i32` + //| NOTE found type `&std::string::String` + //| NOTE try with `&mut y` + let f; + f = box f; + //^ ERROR E0308 + //| NOTE expected type `_` + //| NOTE found type `Box<_>` +} -- cgit 1.4.1-3-g733a5