diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_def/src/data.rs | 17 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 23 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/match_check.rs | 28 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/match_check/usefulness.rs | 5 | ||||
-rw-r--r-- | crates/hir_ty/src/infer.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/pat.rs | 7 | ||||
-rw-r--r-- | crates/hir_ty/src/method_resolution.rs | 16 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/method_resolution.rs | 49 |
8 files changed, 130 insertions, 17 deletions
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index d2bb381be..52cb7777b 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -143,6 +143,10 @@ pub struct TraitData { | |||
143 | pub is_auto: bool, | 143 | pub is_auto: bool, |
144 | pub is_unsafe: bool, | 144 | pub is_unsafe: bool, |
145 | pub visibility: RawVisibility, | 145 | pub visibility: RawVisibility, |
146 | /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore | ||
147 | /// method calls to this trait's methods when the receiver is an array and the crate edition is | ||
148 | /// 2015 or 2018. | ||
149 | pub skip_array_during_method_dispatch: bool, | ||
146 | } | 150 | } |
147 | 151 | ||
148 | impl TraitData { | 152 | impl TraitData { |
@@ -157,6 +161,10 @@ impl TraitData { | |||
157 | let container = AssocContainerId::TraitId(tr); | 161 | let container = AssocContainerId::TraitId(tr); |
158 | let visibility = item_tree[tr_def.visibility].clone(); | 162 | let visibility = item_tree[tr_def.visibility].clone(); |
159 | let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id); | 163 | let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id); |
164 | let skip_array_during_method_dispatch = item_tree | ||
165 | .attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into()) | ||
166 | .by_key("rustc_skip_array_during_method_dispatch") | ||
167 | .exists(); | ||
160 | 168 | ||
161 | let items = collect_items( | 169 | let items = collect_items( |
162 | db, | 170 | db, |
@@ -168,7 +176,14 @@ impl TraitData { | |||
168 | 100, | 176 | 100, |
169 | ); | 177 | ); |
170 | 178 | ||
171 | Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility }) | 179 | Arc::new(TraitData { |
180 | name, | ||
181 | items, | ||
182 | is_auto, | ||
183 | is_unsafe, | ||
184 | visibility, | ||
185 | skip_array_during_method_dispatch, | ||
186 | }) | ||
172 | } | 187 | } |
173 | 188 | ||
174 | pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ { | 189 | pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ { |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 3efbce773..a2a4d61db 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -357,17 +357,20 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
357 | infer: &infer, | 357 | infer: &infer, |
358 | db, | 358 | db, |
359 | pattern_arena: &pattern_arena, | 359 | pattern_arena: &pattern_arena, |
360 | eprint_panic_context: &|| { | 360 | panic_context: &|| { |
361 | use syntax::AstNode; | 361 | use syntax::AstNode; |
362 | if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) { | 362 | let match_expr_text = source_map |
363 | let root = scrutinee_sptr.file_syntax(db.upcast()); | 363 | .expr_syntax(match_expr) |
364 | if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() { | 364 | .ok() |
365 | eprintln!( | 365 | .and_then(|scrutinee_sptr| { |
366 | "Match checking is about to panic on this expression:\n{}", | 366 | let root = scrutinee_sptr.file_syntax(db.upcast()); |
367 | match_ast.to_string(), | 367 | scrutinee_sptr.value.to_node(&root).syntax().parent() |
368 | ); | 368 | }) |
369 | } | 369 | .map(|node| node.to_string()); |
370 | } | 370 | format!( |
371 | "expression:\n{}", | ||
372 | match_expr_text.as_deref().unwrap_or("<synthesized expr>") | ||
373 | ) | ||
371 | }, | 374 | }, |
372 | }; | 375 | }; |
373 | let report = compute_match_usefulness(&cx, &m_arms); | 376 | let report = compute_match_usefulness(&cx, &m_arms); |
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index a9a99f57a..c8e1b23de 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> { | |||
100 | } | 100 | } |
101 | 101 | ||
102 | pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { | 102 | pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { |
103 | // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics") | 103 | // XXX(iDawer): Collecting pattern adjustments feels imprecise to me. |
104 | // When lowering of & and box patterns are implemented this should be tested | ||
105 | // in a manner of `match_ergonomics_issue_9095` test. | ||
106 | // Pattern adjustment is part of RFC 2005-match-ergonomics. | ||
104 | // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 | 107 | // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 |
105 | let unadjusted_pat = self.lower_pattern_unadjusted(pat); | 108 | let unadjusted_pat = self.lower_pattern_unadjusted(pat); |
106 | unadjusted_pat | 109 | self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( |
110 | unadjusted_pat, | ||
111 | |subpattern, ref_ty| Pat { | ||
112 | ty: ref_ty.clone(), | ||
113 | kind: Box::new(PatKind::Deref { subpattern }), | ||
114 | }, | ||
115 | ) | ||
107 | } | 116 | } |
108 | 117 | ||
109 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { | 118 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { |
@@ -1236,6 +1245,21 @@ fn main(f: Foo) { | |||
1236 | ); | 1245 | ); |
1237 | } | 1246 | } |
1238 | 1247 | ||
1248 | #[test] | ||
1249 | fn match_ergonomics_issue_9095() { | ||
1250 | check_diagnostics( | ||
1251 | r#" | ||
1252 | enum Foo<T> { A(T) } | ||
1253 | fn main() { | ||
1254 | match &Foo::A(true) { | ||
1255 | _ => {} | ||
1256 | Foo::A(_) => {} | ||
1257 | } | ||
1258 | } | ||
1259 | "#, | ||
1260 | ); | ||
1261 | } | ||
1262 | |||
1239 | mod false_negatives { | 1263 | mod false_negatives { |
1240 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 1264 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
1241 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 1265 | //! prefer false negatives to false positives (ideally there would be no false positives). This |
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs index 83b094a89..bd76a606c 100644 --- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs | |||
@@ -295,7 +295,7 @@ pub(crate) struct MatchCheckCtx<'a> { | |||
295 | pub(crate) db: &'a dyn HirDatabase, | 295 | pub(crate) db: &'a dyn HirDatabase, |
296 | /// Lowered patterns from arms plus generated by the check. | 296 | /// Lowered patterns from arms plus generated by the check. |
297 | pub(crate) pattern_arena: &'a RefCell<PatternArena>, | 297 | pub(crate) pattern_arena: &'a RefCell<PatternArena>, |
298 | pub(crate) eprint_panic_context: &'a dyn Fn(), | 298 | pub(crate) panic_context: &'a dyn Fn() -> String, |
299 | } | 299 | } |
300 | 300 | ||
301 | impl<'a> MatchCheckCtx<'a> { | 301 | impl<'a> MatchCheckCtx<'a> { |
@@ -331,8 +331,7 @@ impl<'a> MatchCheckCtx<'a> { | |||
331 | 331 | ||
332 | #[track_caller] | 332 | #[track_caller] |
333 | pub(super) fn bug(&self, info: &str) -> ! { | 333 | pub(super) fn bug(&self, info: &str) -> ! { |
334 | (self.eprint_panic_context)(); | 334 | panic!("bug: {}\n{}", info, (self.panic_context)()); |
335 | panic!("bug: {}", info); | ||
336 | } | 335 | } |
337 | } | 336 | } |
338 | 337 | ||
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 7a4268819..0e9f777da 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -150,6 +150,8 @@ pub struct InferenceResult { | |||
150 | type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, | 150 | type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, |
151 | /// Interned Unknown to return references to. | 151 | /// Interned Unknown to return references to. |
152 | standard_types: InternedStandardTypes, | 152 | standard_types: InternedStandardTypes, |
153 | /// Stores the types which were implicitly dereferenced in pattern binding modes. | ||
154 | pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>, | ||
153 | } | 155 | } |
154 | 156 | ||
155 | impl InferenceResult { | 157 | impl InferenceResult { |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index c97b49544..25dff7e49 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -101,7 +101,9 @@ impl<'a> InferenceContext<'a> { | |||
101 | let mut expected = self.resolve_ty_shallow(expected); | 101 | let mut expected = self.resolve_ty_shallow(expected); |
102 | 102 | ||
103 | if is_non_ref_pat(&body, pat) { | 103 | if is_non_ref_pat(&body, pat) { |
104 | let mut pat_adjustments = Vec::new(); | ||
104 | while let Some((inner, _lifetime, mutability)) = expected.as_reference() { | 105 | while let Some((inner, _lifetime, mutability)) = expected.as_reference() { |
106 | pat_adjustments.push(expected.clone()); | ||
105 | expected = self.resolve_ty_shallow(inner); | 107 | expected = self.resolve_ty_shallow(inner); |
106 | default_bm = match default_bm { | 108 | default_bm = match default_bm { |
107 | BindingMode::Move => BindingMode::Ref(mutability), | 109 | BindingMode::Move => BindingMode::Ref(mutability), |
@@ -109,6 +111,11 @@ impl<'a> InferenceContext<'a> { | |||
109 | BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), | 111 | BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), |
110 | } | 112 | } |
111 | } | 113 | } |
114 | |||
115 | if !pat_adjustments.is_empty() { | ||
116 | pat_adjustments.shrink_to_fit(); | ||
117 | self.result.pat_adjustments.insert(pat, pat_adjustments); | ||
118 | } | ||
112 | } else if let Pat::Ref { .. } = &body[pat] { | 119 | } else if let Pat::Ref { .. } = &body[pat] { |
113 | cov_mark::hit!(match_ergonomics_ref); | 120 | cov_mark::hit!(match_ergonomics_ref); |
114 | // When you encounter a `&pat` pattern, reset to Move. | 121 | // When you encounter a `&pat` pattern, reset to Move. |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index af6b6cda7..a23527f7d 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | use std::{iter, sync::Arc}; | 5 | use std::{iter, sync::Arc}; |
6 | 6 | ||
7 | use arrayvec::ArrayVec; | 7 | use arrayvec::ArrayVec; |
8 | use base_db::CrateId; | 8 | use base_db::{CrateId, Edition}; |
9 | use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; | 9 | use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; |
10 | use hir_def::{ | 10 | use hir_def::{ |
11 | lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, | 11 | lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, |
@@ -639,6 +639,7 @@ fn iterate_trait_method_candidates( | |||
639 | receiver_ty: Option<&Canonical<Ty>>, | 639 | receiver_ty: Option<&Canonical<Ty>>, |
640 | callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, | 640 | callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, |
641 | ) -> bool { | 641 | ) -> bool { |
642 | let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..)); | ||
642 | // if ty is `dyn Trait`, the trait doesn't need to be in scope | 643 | // if ty is `dyn Trait`, the trait doesn't need to be in scope |
643 | let inherent_trait = | 644 | let inherent_trait = |
644 | self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); | 645 | self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); |
@@ -655,6 +656,19 @@ fn iterate_trait_method_candidates( | |||
655 | 'traits: for t in traits { | 656 | 'traits: for t in traits { |
656 | let data = db.trait_data(t); | 657 | let data = db.trait_data(t); |
657 | 658 | ||
659 | // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during | ||
660 | // method resolution, if the receiver is an array, and we're compiling for editions before | ||
661 | // 2021. | ||
662 | // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for | ||
663 | // arrays. | ||
664 | if data.skip_array_during_method_dispatch && receiver_is_array { | ||
665 | // FIXME: this should really be using the edition of the method name's span, in case it | ||
666 | // comes from a macro | ||
667 | if db.crate_graph()[krate].edition < Edition::Edition2021 { | ||
668 | continue; | ||
669 | } | ||
670 | } | ||
671 | |||
658 | // we'll be lazy about checking whether the type implements the | 672 | // we'll be lazy about checking whether the type implements the |
659 | // trait, but if we find out it doesn't, we'll skip the rest of the | 673 | // trait, but if we find out it doesn't, we'll skip the rest of the |
660 | // iteration | 674 | // iteration |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 058eb9129..f26b2c8a7 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -1349,3 +1349,52 @@ fn f() { | |||
1349 | "#, | 1349 | "#, |
1350 | ); | 1350 | ); |
1351 | } | 1351 | } |
1352 | |||
1353 | #[test] | ||
1354 | fn skip_array_during_method_dispatch() { | ||
1355 | check_types( | ||
1356 | r#" | ||
1357 | //- /main2018.rs crate:main2018 deps:core | ||
1358 | use core::IntoIterator; | ||
1359 | |||
1360 | fn f() { | ||
1361 | let v = [4].into_iter(); | ||
1362 | v; | ||
1363 | //^ &i32 | ||
1364 | |||
1365 | let a = [0, 1].into_iter(); | ||
1366 | a; | ||
1367 | //^ &i32 | ||
1368 | } | ||
1369 | |||
1370 | //- /main2021.rs crate:main2021 deps:core edition:2021 | ||
1371 | use core::IntoIterator; | ||
1372 | |||
1373 | fn f() { | ||
1374 | let v = [4].into_iter(); | ||
1375 | v; | ||
1376 | //^ i32 | ||
1377 | |||
1378 | let a = [0, 1].into_iter(); | ||
1379 | a; | ||
1380 | //^ &i32 | ||
1381 | } | ||
1382 | |||
1383 | //- /core.rs crate:core | ||
1384 | #[rustc_skip_array_during_method_dispatch] | ||
1385 | pub trait IntoIterator { | ||
1386 | type Out; | ||
1387 | fn into_iter(self) -> Self::Out; | ||
1388 | } | ||
1389 | |||
1390 | impl<T> IntoIterator for [T; 1] { | ||
1391 | type Out = T; | ||
1392 | fn into_iter(self) -> Self::Out {} | ||
1393 | } | ||
1394 | impl<'a, T> IntoIterator for &'a [T] { | ||
1395 | type Out = &'a T; | ||
1396 | fn into_iter(self) -> Self::Out {} | ||
1397 | } | ||
1398 | "#, | ||
1399 | ); | ||
1400 | } | ||