diff options
author | Galilée 'Bill' Enguehard <[email protected]> | 2020-05-21 22:27:38 +0100 |
---|---|---|
committer | Galilée 'Bill' Enguehard <[email protected]> | 2020-05-21 22:27:38 +0100 |
commit | 7fece3bdd2450c0807f7dd742239cae95f0cc65e (patch) | |
tree | 866c4db826c959e79c63a6727bdb9f2c61e6fc4f /crates/ra_hir_ty/src/infer.rs | |
parent | db926218b2082077750291f8426ddd28b284cd08 (diff) | |
parent | 59732df8d40dfadc6dcf5951265416576399712a (diff) |
Merge branch 'master' of github.com:rust-analyzer/rust-analyzer into modname_spacing
Diffstat (limited to 'crates/ra_hir_ty/src/infer.rs')
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 148 |
1 files changed, 132 insertions, 16 deletions
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index bd4ef69a0..957d6e0b5 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -22,13 +22,14 @@ use rustc_hash::FxHashMap; | |||
22 | 22 | ||
23 | use hir_def::{ | 23 | use hir_def::{ |
24 | body::Body, | 24 | body::Body, |
25 | data::{ConstData, FunctionData}, | 25 | data::{ConstData, FunctionData, StaticData}, |
26 | expr::{BindingAnnotation, ExprId, PatId}, | 26 | expr::{BindingAnnotation, ExprId, PatId}, |
27 | lang_item::LangItemTarget, | 27 | lang_item::LangItemTarget, |
28 | path::{path, Path}, | 28 | path::{path, Path}, |
29 | resolver::{HasResolver, Resolver, TypeNs}, | 29 | resolver::{HasResolver, Resolver, TypeNs}, |
30 | type_ref::{Mutability, TypeRef}, | 30 | type_ref::{Mutability, TypeRef}, |
31 | AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, TraitId, TypeAliasId, VariantId, | 31 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, TraitId, TypeAliasId, |
32 | VariantId, | ||
32 | }; | 33 | }; |
33 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; | 34 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; |
34 | use ra_arena::map::ArenaMap; | 35 | use ra_arena::map::ArenaMap; |
@@ -71,7 +72,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer | |||
71 | match def { | 72 | match def { |
72 | DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)), | 73 | DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)), |
73 | DefWithBodyId::FunctionId(f) => ctx.collect_fn(&db.function_data(f)), | 74 | DefWithBodyId::FunctionId(f) => ctx.collect_fn(&db.function_data(f)), |
74 | DefWithBodyId::StaticId(s) => ctx.collect_const(&db.static_data(s)), | 75 | DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), |
75 | } | 76 | } |
76 | 77 | ||
77 | ctx.infer_body(); | 78 | ctx.infer_body(); |
@@ -210,6 +211,14 @@ struct InferenceContext<'a> { | |||
210 | /// closures, but currently this is the only field that will change there, | 211 | /// closures, but currently this is the only field that will change there, |
211 | /// so it doesn't make sense. | 212 | /// so it doesn't make sense. |
212 | return_ty: Ty, | 213 | return_ty: Ty, |
214 | diverges: Diverges, | ||
215 | breakables: Vec<BreakableContext>, | ||
216 | } | ||
217 | |||
218 | #[derive(Clone, Debug)] | ||
219 | struct BreakableContext { | ||
220 | pub may_break: bool, | ||
221 | pub break_ty: Ty, | ||
213 | } | 222 | } |
214 | 223 | ||
215 | impl<'a> InferenceContext<'a> { | 224 | impl<'a> InferenceContext<'a> { |
@@ -224,6 +233,8 @@ impl<'a> InferenceContext<'a> { | |||
224 | owner, | 233 | owner, |
225 | body: db.body(owner), | 234 | body: db.body(owner), |
226 | resolver, | 235 | resolver, |
236 | diverges: Diverges::Maybe, | ||
237 | breakables: Vec::new(), | ||
227 | } | 238 | } |
228 | } | 239 | } |
229 | 240 | ||
@@ -429,43 +440,95 @@ impl<'a> InferenceContext<'a> { | |||
429 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); | 440 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); |
430 | // FIXME: this should resolve assoc items as well, see this example: | 441 | // FIXME: this should resolve assoc items as well, see this example: |
431 | // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 | 442 | // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 |
432 | return match resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path()) { | 443 | let (resolution, unresolved) = |
433 | Some(TypeNs::AdtId(AdtId::StructId(strukt))) => { | 444 | match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { |
445 | Some(it) => it, | ||
446 | None => return (Ty::Unknown, None), | ||
447 | }; | ||
448 | return match resolution { | ||
449 | TypeNs::AdtId(AdtId::StructId(strukt)) => { | ||
434 | let substs = Ty::substs_from_path(&ctx, path, strukt.into()); | 450 | let substs = Ty::substs_from_path(&ctx, path, strukt.into()); |
435 | let ty = self.db.ty(strukt.into()); | 451 | let ty = self.db.ty(strukt.into()); |
436 | let ty = self.insert_type_vars(ty.subst(&substs)); | 452 | let ty = self.insert_type_vars(ty.subst(&substs)); |
437 | (ty, Some(strukt.into())) | 453 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) |
438 | } | 454 | } |
439 | Some(TypeNs::EnumVariantId(var)) => { | 455 | TypeNs::EnumVariantId(var) => { |
440 | let substs = Ty::substs_from_path(&ctx, path, var.into()); | 456 | let substs = Ty::substs_from_path(&ctx, path, var.into()); |
441 | let ty = self.db.ty(var.parent.into()); | 457 | let ty = self.db.ty(var.parent.into()); |
442 | let ty = self.insert_type_vars(ty.subst(&substs)); | 458 | let ty = self.insert_type_vars(ty.subst(&substs)); |
443 | (ty, Some(var.into())) | 459 | forbid_unresolved_segments((ty, Some(var.into())), unresolved) |
444 | } | 460 | } |
445 | Some(TypeNs::SelfType(impl_id)) => { | 461 | TypeNs::SelfType(impl_id) => { |
446 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | 462 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); |
447 | let substs = Substs::type_params_for_generics(&generics); | 463 | let substs = Substs::type_params_for_generics(&generics); |
448 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | 464 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); |
449 | let variant = ty_variant(&ty); | 465 | match unresolved { |
450 | (ty, variant) | 466 | None => { |
467 | let variant = ty_variant(&ty); | ||
468 | (ty, variant) | ||
469 | } | ||
470 | Some(1) => { | ||
471 | let segment = path.mod_path().segments.last().unwrap(); | ||
472 | // this could be an enum variant or associated type | ||
473 | if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { | ||
474 | let enum_data = self.db.enum_data(enum_id); | ||
475 | if let Some(local_id) = enum_data.variant(segment) { | ||
476 | let variant = EnumVariantId { parent: enum_id, local_id }; | ||
477 | return (ty, Some(variant.into())); | ||
478 | } | ||
479 | } | ||
480 | // FIXME potentially resolve assoc type | ||
481 | (Ty::Unknown, None) | ||
482 | } | ||
483 | Some(_) => { | ||
484 | // FIXME diagnostic | ||
485 | (Ty::Unknown, None) | ||
486 | } | ||
487 | } | ||
451 | } | 488 | } |
452 | Some(TypeNs::TypeAliasId(it)) => { | 489 | TypeNs::TypeAliasId(it) => { |
453 | let substs = Substs::build_for_def(self.db, it) | 490 | let substs = Substs::build_for_def(self.db, it) |
454 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) | 491 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) |
455 | .build(); | 492 | .build(); |
456 | let ty = self.db.ty(it.into()).subst(&substs); | 493 | let ty = self.db.ty(it.into()).subst(&substs); |
457 | let variant = ty_variant(&ty); | 494 | let variant = ty_variant(&ty); |
458 | (ty, variant) | 495 | forbid_unresolved_segments((ty, variant), unresolved) |
496 | } | ||
497 | TypeNs::AdtSelfType(_) => { | ||
498 | // FIXME this could happen in array size expressions, once we're checking them | ||
499 | (Ty::Unknown, None) | ||
500 | } | ||
501 | TypeNs::GenericParam(_) => { | ||
502 | // FIXME potentially resolve assoc type | ||
503 | (Ty::Unknown, None) | ||
504 | } | ||
505 | TypeNs::AdtId(AdtId::EnumId(_)) | ||
506 | | TypeNs::AdtId(AdtId::UnionId(_)) | ||
507 | | TypeNs::BuiltinType(_) | ||
508 | | TypeNs::TraitId(_) => { | ||
509 | // FIXME diagnostic | ||
510 | (Ty::Unknown, None) | ||
459 | } | 511 | } |
460 | Some(_) | None => (Ty::Unknown, None), | ||
461 | }; | 512 | }; |
462 | 513 | ||
514 | fn forbid_unresolved_segments( | ||
515 | result: (Ty, Option<VariantId>), | ||
516 | unresolved: Option<usize>, | ||
517 | ) -> (Ty, Option<VariantId>) { | ||
518 | if unresolved.is_none() { | ||
519 | result | ||
520 | } else { | ||
521 | // FIXME diagnostic | ||
522 | (Ty::Unknown, None) | ||
523 | } | ||
524 | } | ||
525 | |||
463 | fn ty_variant(ty: &Ty) -> Option<VariantId> { | 526 | fn ty_variant(ty: &Ty) -> Option<VariantId> { |
464 | ty.as_adt().and_then(|(adt_id, _)| match adt_id { | 527 | ty.as_adt().and_then(|(adt_id, _)| match adt_id { |
465 | AdtId::StructId(s) => Some(VariantId::StructId(s)), | 528 | AdtId::StructId(s) => Some(VariantId::StructId(s)), |
466 | AdtId::UnionId(u) => Some(VariantId::UnionId(u)), | 529 | AdtId::UnionId(u) => Some(VariantId::UnionId(u)), |
467 | AdtId::EnumId(_) => { | 530 | AdtId::EnumId(_) => { |
468 | // Error E0071, expected struct, variant or union type, found enum `Foo` | 531 | // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` |
469 | None | 532 | None |
470 | } | 533 | } |
471 | }) | 534 | }) |
@@ -476,6 +539,10 @@ impl<'a> InferenceContext<'a> { | |||
476 | self.return_ty = self.make_ty(&data.type_ref); | 539 | self.return_ty = self.make_ty(&data.type_ref); |
477 | } | 540 | } |
478 | 541 | ||
542 | fn collect_static(&mut self, data: &StaticData) { | ||
543 | self.return_ty = self.make_ty(&data.type_ref); | ||
544 | } | ||
545 | |||
479 | fn collect_fn(&mut self, data: &FunctionData) { | 546 | fn collect_fn(&mut self, data: &FunctionData) { |
480 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 547 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
481 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) | 548 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) |
@@ -666,15 +733,57 @@ impl Expectation { | |||
666 | } | 733 | } |
667 | } | 734 | } |
668 | 735 | ||
736 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
737 | enum Diverges { | ||
738 | Maybe, | ||
739 | Always, | ||
740 | } | ||
741 | |||
742 | impl Diverges { | ||
743 | fn is_always(self) -> bool { | ||
744 | self == Diverges::Always | ||
745 | } | ||
746 | } | ||
747 | |||
748 | impl std::ops::BitAnd for Diverges { | ||
749 | type Output = Self; | ||
750 | fn bitand(self, other: Self) -> Self { | ||
751 | std::cmp::min(self, other) | ||
752 | } | ||
753 | } | ||
754 | |||
755 | impl std::ops::BitOr for Diverges { | ||
756 | type Output = Self; | ||
757 | fn bitor(self, other: Self) -> Self { | ||
758 | std::cmp::max(self, other) | ||
759 | } | ||
760 | } | ||
761 | |||
762 | impl std::ops::BitAndAssign for Diverges { | ||
763 | fn bitand_assign(&mut self, other: Self) { | ||
764 | *self = *self & other; | ||
765 | } | ||
766 | } | ||
767 | |||
768 | impl std::ops::BitOrAssign for Diverges { | ||
769 | fn bitor_assign(&mut self, other: Self) { | ||
770 | *self = *self | other; | ||
771 | } | ||
772 | } | ||
773 | |||
669 | mod diagnostics { | 774 | mod diagnostics { |
670 | use hir_def::{expr::ExprId, FunctionId}; | 775 | use hir_def::{expr::ExprId, FunctionId}; |
671 | use hir_expand::diagnostics::DiagnosticSink; | 776 | use hir_expand::diagnostics::DiagnosticSink; |
672 | 777 | ||
673 | use crate::{db::HirDatabase, diagnostics::NoSuchField}; | 778 | use crate::{ |
779 | db::HirDatabase, | ||
780 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, | ||
781 | }; | ||
674 | 782 | ||
675 | #[derive(Debug, PartialEq, Eq, Clone)] | 783 | #[derive(Debug, PartialEq, Eq, Clone)] |
676 | pub(super) enum InferenceDiagnostic { | 784 | pub(super) enum InferenceDiagnostic { |
677 | NoSuchField { expr: ExprId, field: usize }, | 785 | NoSuchField { expr: ExprId, field: usize }, |
786 | BreakOutsideOfLoop { expr: ExprId }, | ||
678 | } | 787 | } |
679 | 788 | ||
680 | impl InferenceDiagnostic { | 789 | impl InferenceDiagnostic { |
@@ -690,6 +799,13 @@ mod diagnostics { | |||
690 | let field = source_map.field_syntax(*expr, *field); | 799 | let field = source_map.field_syntax(*expr, *field); |
691 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | 800 | sink.push(NoSuchField { file: field.file_id, field: field.value }) |
692 | } | 801 | } |
802 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
803 | let (_, source_map) = db.body_with_source_map(owner.into()); | ||
804 | let ptr = source_map | ||
805 | .expr_syntax(*expr) | ||
806 | .expect("break outside of loop in synthetic syntax"); | ||
807 | sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) | ||
808 | } | ||
693 | } | 809 | } |
694 | } | 810 | } |
695 | } | 811 | } |