]> git.proxmox.com Git - rustc.git/blob - src/librustc_typeck/check/demand.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / librustc_typeck / check / demand.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11
12 use check::FnCtxt;
13 use rustc::infer::InferOk;
14 use rustc::traits::ObligationCause;
15
16 use syntax::ast;
17 use syntax_pos::{self, Span};
18 use rustc::hir;
19 use rustc::hir::print;
20 use rustc::hir::def::Def;
21 use rustc::ty::{self, Ty, AssociatedItem};
22 use errors::{DiagnosticBuilder, CodeMapper};
23
24 use super::method::probe;
25
26 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
27 // Requires that the two types unify, and prints an error message if
28 // they don't.
29 pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
30 self.demand_suptype_diag(sp, expected, actual).map(|mut e| e.emit());
31 }
32
33 pub fn demand_suptype_diag(&self,
34 sp: Span,
35 expected: Ty<'tcx>,
36 actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
37 let cause = &self.misc(sp);
38 match self.at(cause, self.param_env).sup(expected, actual) {
39 Ok(InferOk { obligations, value: () }) => {
40 self.register_predicates(obligations);
41 None
42 },
43 Err(e) => {
44 Some(self.report_mismatched_types(&cause, expected, actual, e))
45 }
46 }
47 }
48
49 pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
50 if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
51 err.emit();
52 }
53 }
54
55 pub fn demand_eqtype_diag(&self,
56 sp: Span,
57 expected: Ty<'tcx>,
58 actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
59 self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
60 }
61
62 pub fn demand_eqtype_with_origin(&self,
63 cause: &ObligationCause<'tcx>,
64 expected: Ty<'tcx>,
65 actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
66 match self.at(cause, self.param_env).eq(expected, actual) {
67 Ok(InferOk { obligations, value: () }) => {
68 self.register_predicates(obligations);
69 None
70 }
71 Err(e) => {
72 Some(self.report_mismatched_types(cause, expected, actual, e))
73 }
74 }
75 }
76
77 pub fn demand_coerce(&self,
78 expr: &hir::Expr,
79 checked_ty: Ty<'tcx>,
80 expected: Ty<'tcx>)
81 -> Ty<'tcx> {
82 let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected);
83 if let Some(mut err) = err {
84 err.emit();
85 }
86 ty
87 }
88
89 // Checks that the type of `expr` can be coerced to `expected`.
90 //
91 // NB: This code relies on `self.diverges` to be accurate. In
92 // particular, assignments to `!` will be permitted if the
93 // diverges flag is currently "always".
94 pub fn demand_coerce_diag(&self,
95 expr: &hir::Expr,
96 checked_ty: Ty<'tcx>,
97 expected: Ty<'tcx>)
98 -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
99 let expected = self.resolve_type_vars_with_obligations(expected);
100
101 let e = match self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
102 Ok(ty) => return (ty, None),
103 Err(e) => e
104 };
105
106 let cause = self.misc(expr.span);
107 let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
108 let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
109
110 // If the expected type is an enum with any variants whose sole
111 // field is of the found type, suggest such variants. See Issue
112 // #42764.
113 if let ty::TyAdt(expected_adt, substs) = expected.sty {
114 let mut compatible_variants = vec![];
115 for variant in &expected_adt.variants {
116 if variant.fields.len() == 1 {
117 let sole_field = &variant.fields[0];
118 let sole_field_ty = sole_field.ty(self.tcx, substs);
119 if self.can_coerce(expr_ty, sole_field_ty) {
120 let mut variant_path = self.tcx.item_path_str(variant.did);
121 variant_path = variant_path.trim_left_matches("std::prelude::v1::")
122 .to_string();
123 compatible_variants.push(variant_path);
124 }
125 }
126 }
127 if !compatible_variants.is_empty() {
128 let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr));
129 let suggestions = compatible_variants.iter()
130 .map(|v| format!("{}({})", v, expr_text)).collect::<Vec<_>>();
131 err.span_suggestions(expr.span,
132 "try using a variant of the expected type",
133 suggestions);
134 }
135 }
136
137 if let Some(suggestion) = self.check_ref(expr,
138 checked_ty,
139 expected) {
140 err.help(&suggestion);
141 } else {
142 let mode = probe::Mode::MethodCall;
143 let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP,
144 mode,
145 expected,
146 checked_ty,
147 ast::DUMMY_NODE_ID);
148 if suggestions.len() > 0 {
149 err.help(&format!("here are some functions which \
150 might fulfill your needs:\n{}",
151 self.get_best_match(&suggestions).join("\n")));
152 }
153 }
154 (expected, Some(err))
155 }
156
157 fn format_method_suggestion(&self, method: &AssociatedItem) -> String {
158 format!("- .{}({})",
159 method.name,
160 if self.has_no_input_arg(method) {
161 ""
162 } else {
163 "..."
164 })
165 }
166
167 fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> Vec<String> {
168 methods.iter()
169 .take(5)
170 .map(|method| self.format_method_suggestion(&*method))
171 .collect::<Vec<String>>()
172 }
173
174 fn get_best_match(&self, methods: &[AssociatedItem]) -> Vec<String> {
175 let no_argument_methods: Vec<_> =
176 methods.iter()
177 .filter(|ref x| self.has_no_input_arg(&*x))
178 .map(|x| x.clone())
179 .collect();
180 if no_argument_methods.len() > 0 {
181 self.display_suggested_methods(&no_argument_methods)
182 } else {
183 self.display_suggested_methods(&methods)
184 }
185 }
186
187 // This function checks if the method isn't static and takes other arguments than `self`.
188 fn has_no_input_arg(&self, method: &AssociatedItem) -> bool {
189 match method.def() {
190 Def::Method(def_id) => {
191 self.tcx.fn_sig(def_id).inputs().skip_binder().len() == 1
192 }
193 _ => false,
194 }
195 }
196
197 /// This function is used to determine potential "simple" improvements or users' errors and
198 /// provide them useful help. For example:
199 ///
200 /// ```
201 /// fn some_fn(s: &str) {}
202 ///
203 /// let x = "hey!".to_owned();
204 /// some_fn(x); // error
205 /// ```
206 ///
207 /// No need to find every potential function which could make a coercion to transform a
208 /// `String` into a `&str` since a `&` would do the trick!
209 ///
210 /// In addition of this check, it also checks between references mutability state. If the
211 /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
212 /// `&mut`!".
213 fn check_ref(&self,
214 expr: &hir::Expr,
215 checked_ty: Ty<'tcx>,
216 expected: Ty<'tcx>)
217 -> Option<String> {
218 match (&expected.sty, &checked_ty.sty) {
219 (&ty::TyRef(_, exp), &ty::TyRef(_, check)) => match (&exp.ty.sty, &check.ty.sty) {
220 (&ty::TyStr, &ty::TyArray(arr, _)) |
221 (&ty::TyStr, &ty::TySlice(arr)) if arr == self.tcx.types.u8 => {
222 if let hir::ExprLit(_) = expr.node {
223 let sp = self.sess().codemap().call_span_if_macro(expr.span);
224 if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
225 return Some(format!("try `{}`", &src[1..]));
226 }
227 }
228 None
229 },
230 (&ty::TyArray(arr, _), &ty::TyStr) |
231 (&ty::TySlice(arr), &ty::TyStr) if arr == self.tcx.types.u8 => {
232 if let hir::ExprLit(_) = expr.node {
233 let sp = self.sess().codemap().call_span_if_macro(expr.span);
234 if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
235 return Some(format!("try `b{}`", src));
236 }
237 }
238 None
239 }
240 _ => None,
241 },
242 (&ty::TyRef(_, mutability), _) => {
243 // Check if it can work when put into a ref. For example:
244 //
245 // ```
246 // fn bar(x: &mut i32) {}
247 //
248 // let x = 0u32;
249 // bar(&x); // error, expected &mut
250 // ```
251 let ref_ty = match mutability.mutbl {
252 hir::Mutability::MutMutable => self.tcx.mk_mut_ref(
253 self.tcx.mk_region(ty::ReStatic),
254 checked_ty),
255 hir::Mutability::MutImmutable => self.tcx.mk_imm_ref(
256 self.tcx.mk_region(ty::ReStatic),
257 checked_ty),
258 };
259 if self.can_coerce(ref_ty, expected) {
260 // Use the callsite's span if this is a macro call. #41858
261 let sp = self.sess().codemap().call_span_if_macro(expr.span);
262 if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
263 return Some(format!("try with `{}{}`",
264 match mutability.mutbl {
265 hir::Mutability::MutMutable => "&mut ",
266 hir::Mutability::MutImmutable => "&",
267 },
268 &src));
269 }
270 }
271 None
272 }
273 (_, &ty::TyRef(_, checked)) => {
274 // We have `&T`, check if what was expected was `T`. If so,
275 // we may want to suggest adding a `*`, or removing
276 // a `&`.
277 //
278 // (But, also check check the `expn_info()` to see if this is
279 // a macro; if so, it's hard to extract the text and make a good
280 // suggestion, so don't bother.)
281 if self.infcx.can_sub(self.param_env, checked.ty, &expected).is_ok() &&
282 expr.span.ctxt().outer().expn_info().is_none() {
283 match expr.node {
284 // Maybe remove `&`?
285 hir::ExprAddrOf(_, ref expr) => {
286 if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
287 return Some(format!("try with `{}`", code));
288 }
289 }
290
291 // Maybe add `*`? Only if `T: Copy`.
292 _ => {
293 if !self.infcx.type_moves_by_default(self.param_env,
294 checked.ty,
295 expr.span) {
296 let sp = self.sess().codemap().call_span_if_macro(expr.span);
297 if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(sp) {
298 return Some(format!("try with `*{}`", code));
299 }
300 }
301 },
302 }
303 }
304 None
305 }
306 _ => None,
307 }
308 }
309 }