diff options
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r-- | crates/ra_hir_ty/Cargo.toml | 6 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/_match.rs | 336 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/db.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/pat.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/test_db.rs | 36 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests.rs | 52 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/patterns.rs | 26 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/regression.rs | 41 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits/chalk.rs | 145 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits/chalk/tls.rs | 33 |
13 files changed, 605 insertions, 91 deletions
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 177bdbcb0..04d3cd6a2 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -27,9 +27,9 @@ test_utils = { path = "../test_utils" } | |||
27 | 27 | ||
28 | scoped-tls = "1" | 28 | scoped-tls = "1" |
29 | 29 | ||
30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } | 30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } |
31 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } | 31 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } |
32 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" } | 32 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } |
33 | 33 | ||
34 | [dev-dependencies] | 34 | [dev-dependencies] |
35 | insta = "0.16.0" | 35 | insta = "0.16.0" |
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs index 688026a04..779e78574 100644 --- a/crates/ra_hir_ty/src/_match.rs +++ b/crates/ra_hir_ty/src/_match.rs | |||
@@ -235,10 +235,19 @@ impl From<PatId> for PatIdOrWild { | |||
235 | } | 235 | } |
236 | } | 236 | } |
237 | 237 | ||
238 | impl From<&PatId> for PatIdOrWild { | ||
239 | fn from(pat_id: &PatId) -> Self { | ||
240 | Self::PatId(*pat_id) | ||
241 | } | ||
242 | } | ||
243 | |||
238 | #[derive(Debug, Clone, Copy, PartialEq)] | 244 | #[derive(Debug, Clone, Copy, PartialEq)] |
239 | pub enum MatchCheckErr { | 245 | pub enum MatchCheckErr { |
240 | NotImplemented, | 246 | NotImplemented, |
241 | MalformedMatchArm, | 247 | MalformedMatchArm, |
248 | /// Used when type inference cannot resolve the type of | ||
249 | /// a pattern or expression. | ||
250 | Unknown, | ||
242 | } | 251 | } |
243 | 252 | ||
244 | /// The return type of `is_useful` is either an indication of usefulness | 253 | /// The return type of `is_useful` is either an indication of usefulness |
@@ -290,10 +299,14 @@ impl PatStack { | |||
290 | Self::from_slice(&self.0[1..]) | 299 | Self::from_slice(&self.0[1..]) |
291 | } | 300 | } |
292 | 301 | ||
293 | fn replace_head_with<T: Into<PatIdOrWild> + Copy>(&self, pat_ids: &[T]) -> PatStack { | 302 | fn replace_head_with<I, T>(&self, pats: I) -> PatStack |
303 | where | ||
304 | I: Iterator<Item = T>, | ||
305 | T: Into<PatIdOrWild>, | ||
306 | { | ||
294 | let mut patterns: PatStackInner = smallvec![]; | 307 | let mut patterns: PatStackInner = smallvec![]; |
295 | for pat in pat_ids { | 308 | for pat in pats { |
296 | patterns.push((*pat).into()); | 309 | patterns.push(pat.into()); |
297 | } | 310 | } |
298 | for pat in &self.0[1..] { | 311 | for pat in &self.0[1..] { |
299 | patterns.push(*pat); | 312 | patterns.push(*pat); |
@@ -330,7 +343,7 @@ impl PatStack { | |||
330 | return Err(MatchCheckErr::NotImplemented); | 343 | return Err(MatchCheckErr::NotImplemented); |
331 | } | 344 | } |
332 | 345 | ||
333 | Some(self.replace_head_with(pat_ids)) | 346 | Some(self.replace_head_with(pat_ids.iter())) |
334 | } | 347 | } |
335 | (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { | 348 | (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { |
336 | match cx.body.exprs[lit_expr] { | 349 | match cx.body.exprs[lit_expr] { |
@@ -382,7 +395,7 @@ impl PatStack { | |||
382 | new_patterns.push((*pat_id).into()); | 395 | new_patterns.push((*pat_id).into()); |
383 | } | 396 | } |
384 | 397 | ||
385 | Some(self.replace_head_with(&new_patterns)) | 398 | Some(self.replace_head_with(new_patterns.into_iter())) |
386 | } else { | 399 | } else { |
387 | return Err(MatchCheckErr::MalformedMatchArm); | 400 | return Err(MatchCheckErr::MalformedMatchArm); |
388 | } | 401 | } |
@@ -390,13 +403,41 @@ impl PatStack { | |||
390 | // If there is no ellipsis in the tuple pattern, the number | 403 | // If there is no ellipsis in the tuple pattern, the number |
391 | // of patterns must equal the constructor arity. | 404 | // of patterns must equal the constructor arity. |
392 | if pat_ids.len() == constructor_arity { | 405 | if pat_ids.len() == constructor_arity { |
393 | Some(self.replace_head_with(pat_ids)) | 406 | Some(self.replace_head_with(pat_ids.into_iter())) |
394 | } else { | 407 | } else { |
395 | return Err(MatchCheckErr::MalformedMatchArm); | 408 | return Err(MatchCheckErr::MalformedMatchArm); |
396 | } | 409 | } |
397 | } | 410 | } |
398 | } | 411 | } |
399 | } | 412 | } |
413 | (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { | ||
414 | let pat_id = self.head().as_id().expect("we know this isn't a wild"); | ||
415 | if !enum_variant_matches(cx, pat_id, *e) { | ||
416 | None | ||
417 | } else { | ||
418 | match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { | ||
419 | VariantData::Record(struct_field_arena) => { | ||
420 | // Here we treat any missing fields in the record as the wild pattern, as | ||
421 | // if the record has ellipsis. We want to do this here even if the | ||
422 | // record does not contain ellipsis, because it allows us to continue | ||
423 | // enforcing exhaustiveness for the rest of the match statement. | ||
424 | // | ||
425 | // Creating the diagnostic for the missing field in the pattern | ||
426 | // should be done in a different diagnostic. | ||
427 | let patterns = struct_field_arena.iter().map(|(_, struct_field)| { | ||
428 | arg_patterns | ||
429 | .iter() | ||
430 | .find(|pat| pat.name == struct_field.name) | ||
431 | .map(|pat| PatIdOrWild::from(pat.pat)) | ||
432 | .unwrap_or(PatIdOrWild::Wild) | ||
433 | }); | ||
434 | |||
435 | Some(self.replace_head_with(patterns)) | ||
436 | } | ||
437 | _ => return Err(MatchCheckErr::Unknown), | ||
438 | } | ||
439 | } | ||
440 | } | ||
400 | (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented), | 441 | (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented), |
401 | (_, _) => return Err(MatchCheckErr::NotImplemented), | 442 | (_, _) => return Err(MatchCheckErr::NotImplemented), |
402 | }; | 443 | }; |
@@ -655,8 +696,8 @@ impl Constructor { | |||
655 | Constructor::Enum(e) => { | 696 | Constructor::Enum(e) => { |
656 | match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { | 697 | match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { |
657 | VariantData::Tuple(struct_field_data) => struct_field_data.len(), | 698 | VariantData::Tuple(struct_field_data) => struct_field_data.len(), |
699 | VariantData::Record(struct_field_data) => struct_field_data.len(), | ||
658 | VariantData::Unit => 0, | 700 | VariantData::Unit => 0, |
659 | _ => return Err(MatchCheckErr::NotImplemented), | ||
660 | } | 701 | } |
661 | } | 702 | } |
662 | }; | 703 | }; |
@@ -695,10 +736,10 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Opt | |||
695 | Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), | 736 | Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), |
696 | _ => return Err(MatchCheckErr::NotImplemented), | 737 | _ => return Err(MatchCheckErr::NotImplemented), |
697 | }, | 738 | }, |
698 | Pat::TupleStruct { .. } | Pat::Path(_) => { | 739 | Pat::TupleStruct { .. } | Pat::Path(_) | Pat::Record { .. } => { |
699 | let pat_id = pat.as_id().expect("we already know this pattern is not a wild"); | 740 | let pat_id = pat.as_id().expect("we already know this pattern is not a wild"); |
700 | let variant_id = | 741 | let variant_id = |
701 | cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::NotImplemented)?; | 742 | cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::Unknown)?; |
702 | match variant_id { | 743 | match variant_id { |
703 | VariantId::EnumVariantId(enum_variant_id) => { | 744 | VariantId::EnumVariantId(enum_variant_id) => { |
704 | Some(Constructor::Enum(enum_variant_id)) | 745 | Some(Constructor::Enum(enum_variant_id)) |
@@ -759,20 +800,22 @@ mod tests { | |||
759 | pub(super) use insta::assert_snapshot; | 800 | pub(super) use insta::assert_snapshot; |
760 | pub(super) use ra_db::fixture::WithFixture; | 801 | pub(super) use ra_db::fixture::WithFixture; |
761 | 802 | ||
762 | pub(super) use crate::test_db::TestDB; | 803 | pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; |
763 | 804 | ||
764 | pub(super) fn check_diagnostic_message(content: &str) -> String { | 805 | pub(super) fn check_diagnostic_message(content: &str) -> String { |
765 | TestDB::with_single_file(content).0.diagnostics().0 | 806 | TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().0 |
766 | } | 807 | } |
767 | 808 | ||
768 | pub(super) fn check_diagnostic(content: &str) { | 809 | pub(super) fn check_diagnostic(content: &str) { |
769 | let diagnostic_count = TestDB::with_single_file(content).0.diagnostics().1; | 810 | let diagnostic_count = |
811 | TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1; | ||
770 | 812 | ||
771 | assert_eq!(1, diagnostic_count, "no diagnostic reported"); | 813 | assert_eq!(1, diagnostic_count, "no diagnostic reported"); |
772 | } | 814 | } |
773 | 815 | ||
774 | pub(super) fn check_no_diagnostic(content: &str) { | 816 | pub(super) fn check_no_diagnostic(content: &str) { |
775 | let diagnostic_count = TestDB::with_single_file(content).0.diagnostics().1; | 817 | let diagnostic_count = |
818 | TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1; | ||
776 | 819 | ||
777 | assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); | 820 | assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); |
778 | } | 821 | } |
@@ -1532,6 +1575,236 @@ mod tests { | |||
1532 | } | 1575 | } |
1533 | 1576 | ||
1534 | #[test] | 1577 | #[test] |
1578 | fn enum_record_no_arms() { | ||
1579 | let content = r" | ||
1580 | enum Either { | ||
1581 | A { foo: bool }, | ||
1582 | B, | ||
1583 | } | ||
1584 | fn test_fn() { | ||
1585 | let a = Either::A { foo: true }; | ||
1586 | match a { | ||
1587 | } | ||
1588 | } | ||
1589 | "; | ||
1590 | |||
1591 | check_diagnostic(content); | ||
1592 | } | ||
1593 | |||
1594 | #[test] | ||
1595 | fn enum_record_missing_arms() { | ||
1596 | let content = r" | ||
1597 | enum Either { | ||
1598 | A { foo: bool }, | ||
1599 | B, | ||
1600 | } | ||
1601 | fn test_fn() { | ||
1602 | let a = Either::A { foo: true }; | ||
1603 | match a { | ||
1604 | Either::A { foo: true } => (), | ||
1605 | } | ||
1606 | } | ||
1607 | "; | ||
1608 | |||
1609 | check_diagnostic(content); | ||
1610 | } | ||
1611 | |||
1612 | #[test] | ||
1613 | fn enum_record_no_diagnostic() { | ||
1614 | let content = r" | ||
1615 | enum Either { | ||
1616 | A { foo: bool }, | ||
1617 | B, | ||
1618 | } | ||
1619 | fn test_fn() { | ||
1620 | let a = Either::A { foo: true }; | ||
1621 | match a { | ||
1622 | Either::A { foo: true } => (), | ||
1623 | Either::A { foo: false } => (), | ||
1624 | Either::B => (), | ||
1625 | } | ||
1626 | } | ||
1627 | "; | ||
1628 | |||
1629 | check_no_diagnostic(content); | ||
1630 | } | ||
1631 | |||
1632 | #[test] | ||
1633 | fn enum_record_missing_field_no_diagnostic() { | ||
1634 | let content = r" | ||
1635 | enum Either { | ||
1636 | A { foo: bool }, | ||
1637 | B, | ||
1638 | } | ||
1639 | fn test_fn() { | ||
1640 | let a = Either::B; | ||
1641 | match a { | ||
1642 | Either::A { } => (), | ||
1643 | Either::B => (), | ||
1644 | } | ||
1645 | } | ||
1646 | "; | ||
1647 | |||
1648 | // When `Either::A` is missing a struct member, we don't want | ||
1649 | // to fire the missing match arm diagnostic. This should fire | ||
1650 | // some other diagnostic. | ||
1651 | check_no_diagnostic(content); | ||
1652 | } | ||
1653 | |||
1654 | #[test] | ||
1655 | fn enum_record_missing_field_missing_match_arm() { | ||
1656 | let content = r" | ||
1657 | enum Either { | ||
1658 | A { foo: bool }, | ||
1659 | B, | ||
1660 | } | ||
1661 | fn test_fn() { | ||
1662 | let a = Either::B; | ||
1663 | match a { | ||
1664 | Either::A { } => (), | ||
1665 | } | ||
1666 | } | ||
1667 | "; | ||
1668 | |||
1669 | // Even though `Either::A` is missing fields, we still want to fire | ||
1670 | // the missing arm diagnostic here, since we know `Either::B` is missing. | ||
1671 | check_diagnostic(content); | ||
1672 | } | ||
1673 | |||
1674 | #[test] | ||
1675 | fn enum_record_no_diagnostic_wild() { | ||
1676 | let content = r" | ||
1677 | enum Either { | ||
1678 | A { foo: bool }, | ||
1679 | B, | ||
1680 | } | ||
1681 | fn test_fn() { | ||
1682 | let a = Either::A { foo: true }; | ||
1683 | match a { | ||
1684 | Either::A { foo: _ } => (), | ||
1685 | Either::B => (), | ||
1686 | } | ||
1687 | } | ||
1688 | "; | ||
1689 | |||
1690 | check_no_diagnostic(content); | ||
1691 | } | ||
1692 | |||
1693 | #[test] | ||
1694 | fn enum_record_fields_out_of_order_missing_arm() { | ||
1695 | let content = r" | ||
1696 | enum Either { | ||
1697 | A { foo: bool, bar: () }, | ||
1698 | B, | ||
1699 | } | ||
1700 | fn test_fn() { | ||
1701 | let a = Either::A { foo: true }; | ||
1702 | match a { | ||
1703 | Either::A { bar: (), foo: false } => (), | ||
1704 | Either::A { foo: true, bar: () } => (), | ||
1705 | } | ||
1706 | } | ||
1707 | "; | ||
1708 | |||
1709 | check_diagnostic(content); | ||
1710 | } | ||
1711 | |||
1712 | #[test] | ||
1713 | fn enum_record_fields_out_of_order_no_diagnostic() { | ||
1714 | let content = r" | ||
1715 | enum Either { | ||
1716 | A { foo: bool, bar: () }, | ||
1717 | B, | ||
1718 | } | ||
1719 | fn test_fn() { | ||
1720 | let a = Either::A { foo: true }; | ||
1721 | match a { | ||
1722 | Either::A { bar: (), foo: false } => (), | ||
1723 | Either::A { foo: true, bar: () } => (), | ||
1724 | Either::B => (), | ||
1725 | } | ||
1726 | } | ||
1727 | "; | ||
1728 | |||
1729 | check_no_diagnostic(content); | ||
1730 | } | ||
1731 | |||
1732 | #[test] | ||
1733 | fn enum_record_ellipsis_missing_arm() { | ||
1734 | let content = r" | ||
1735 | enum Either { | ||
1736 | A { foo: bool, bar: bool }, | ||
1737 | B, | ||
1738 | } | ||
1739 | fn test_fn() { | ||
1740 | match Either::B { | ||
1741 | Either::A { foo: true, .. } => (), | ||
1742 | Either::B => (), | ||
1743 | } | ||
1744 | } | ||
1745 | "; | ||
1746 | |||
1747 | check_diagnostic(content); | ||
1748 | } | ||
1749 | |||
1750 | #[test] | ||
1751 | fn enum_record_ellipsis_no_diagnostic() { | ||
1752 | let content = r" | ||
1753 | enum Either { | ||
1754 | A { foo: bool, bar: bool }, | ||
1755 | B, | ||
1756 | } | ||
1757 | fn test_fn() { | ||
1758 | let a = Either::A { foo: true }; | ||
1759 | match a { | ||
1760 | Either::A { foo: true, .. } => (), | ||
1761 | Either::A { foo: false, .. } => (), | ||
1762 | Either::B => (), | ||
1763 | } | ||
1764 | } | ||
1765 | "; | ||
1766 | |||
1767 | check_no_diagnostic(content); | ||
1768 | } | ||
1769 | |||
1770 | #[test] | ||
1771 | fn enum_record_ellipsis_all_fields_missing_arm() { | ||
1772 | let content = r" | ||
1773 | enum Either { | ||
1774 | A { foo: bool, bar: bool }, | ||
1775 | B, | ||
1776 | } | ||
1777 | fn test_fn() { | ||
1778 | let a = Either::B; | ||
1779 | match a { | ||
1780 | Either::A { .. } => (), | ||
1781 | } | ||
1782 | } | ||
1783 | "; | ||
1784 | |||
1785 | check_diagnostic(content); | ||
1786 | } | ||
1787 | |||
1788 | #[test] | ||
1789 | fn enum_record_ellipsis_all_fields_no_diagnostic() { | ||
1790 | let content = r" | ||
1791 | enum Either { | ||
1792 | A { foo: bool, bar: bool }, | ||
1793 | B, | ||
1794 | } | ||
1795 | fn test_fn() { | ||
1796 | let a = Either::B; | ||
1797 | match a { | ||
1798 | Either::A { .. } => (), | ||
1799 | Either::B => (), | ||
1800 | } | ||
1801 | } | ||
1802 | "; | ||
1803 | |||
1804 | check_no_diagnostic(content); | ||
1805 | } | ||
1806 | |||
1807 | #[test] | ||
1535 | fn enum_tuple_partial_ellipsis_no_diagnostic() { | 1808 | fn enum_tuple_partial_ellipsis_no_diagnostic() { |
1536 | let content = r" | 1809 | let content = r" |
1537 | enum Either { | 1810 | enum Either { |
@@ -1689,25 +1962,6 @@ mod false_negatives { | |||
1689 | } | 1962 | } |
1690 | 1963 | ||
1691 | #[test] | 1964 | #[test] |
1692 | fn enum_record() { | ||
1693 | let content = r" | ||
1694 | enum Either { | ||
1695 | A { foo: u32 }, | ||
1696 | B, | ||
1697 | } | ||
1698 | fn test_fn() { | ||
1699 | match Either::B { | ||
1700 | Either::A { foo: 5 } => (), | ||
1701 | } | ||
1702 | } | ||
1703 | "; | ||
1704 | |||
1705 | // This is a false negative. | ||
1706 | // We don't currently handle enum record types. | ||
1707 | check_no_diagnostic(content); | ||
1708 | } | ||
1709 | |||
1710 | #[test] | ||
1711 | fn internal_or() { | 1965 | fn internal_or() { |
1712 | let content = r" | 1966 | let content = r" |
1713 | fn test_fn() { | 1967 | fn test_fn() { |
@@ -1796,4 +2050,22 @@ mod false_negatives { | |||
1796 | // We don't currently handle tuple patterns with ellipsis. | 2050 | // We don't currently handle tuple patterns with ellipsis. |
1797 | check_no_diagnostic(content); | 2051 | check_no_diagnostic(content); |
1798 | } | 2052 | } |
2053 | |||
2054 | #[test] | ||
2055 | fn struct_missing_arm() { | ||
2056 | let content = r" | ||
2057 | struct Foo { | ||
2058 | a: bool, | ||
2059 | } | ||
2060 | fn test_fn(f: Foo) { | ||
2061 | match f { | ||
2062 | Foo { a: true } => {}, | ||
2063 | } | ||
2064 | } | ||
2065 | "; | ||
2066 | |||
2067 | // This is a false negative. | ||
2068 | // We don't currently handle structs. | ||
2069 | check_no_diagnostic(content); | ||
2070 | } | ||
1799 | } | 2071 | } |
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index 33da16b48..9e5dfeab3 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs | |||
@@ -107,6 +107,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
107 | krate: CrateId, | 107 | krate: CrateId, |
108 | goal: crate::Canonical<crate::InEnvironment<crate::Obligation>>, | 108 | goal: crate::Canonical<crate::InEnvironment<crate::Obligation>>, |
109 | ) -> Option<crate::traits::Solution>; | 109 | ) -> Option<crate::traits::Solution>; |
110 | |||
111 | #[salsa::invoke(crate::traits::chalk::program_clauses_for_chalk_env_query)] | ||
112 | fn program_clauses_for_chalk_env( | ||
113 | &self, | ||
114 | krate: CrateId, | ||
115 | env: chalk_ir::Environment<chalk::Interner>, | ||
116 | ) -> chalk_ir::ProgramClauses<chalk::Interner>; | ||
110 | } | 117 | } |
111 | 118 | ||
112 | fn infer_wait(db: &impl HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { | 119 | fn infer_wait(db: &impl HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index b6d9b3438..dfb6a435f 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -127,6 +127,7 @@ pub struct InferenceResult { | |||
127 | field_resolutions: FxHashMap<ExprId, StructFieldId>, | 127 | field_resolutions: FxHashMap<ExprId, StructFieldId>, |
128 | /// For each field in record literal, records the field it resolves to. | 128 | /// For each field in record literal, records the field it resolves to. |
129 | record_field_resolutions: FxHashMap<ExprId, StructFieldId>, | 129 | record_field_resolutions: FxHashMap<ExprId, StructFieldId>, |
130 | record_field_pat_resolutions: FxHashMap<PatId, StructFieldId>, | ||
130 | /// For each struct literal, records the variant it resolves to. | 131 | /// For each struct literal, records the variant it resolves to. |
131 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, | 132 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, |
132 | /// For each associated item record what it resolves to | 133 | /// For each associated item record what it resolves to |
@@ -147,6 +148,9 @@ impl InferenceResult { | |||
147 | pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructFieldId> { | 148 | pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructFieldId> { |
148 | self.record_field_resolutions.get(&expr).copied() | 149 | self.record_field_resolutions.get(&expr).copied() |
149 | } | 150 | } |
151 | pub fn record_field_pat_resolution(&self, pat: PatId) -> Option<StructFieldId> { | ||
152 | self.record_field_pat_resolutions.get(&pat).copied() | ||
153 | } | ||
150 | pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> { | 154 | pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> { |
151 | self.variant_resolutions.get(&id.into()).copied() | 155 | self.variant_resolutions.get(&id.into()).copied() |
152 | } | 156 | } |
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index 8ec4d4ace..7c2ad4384 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs | |||
@@ -7,6 +7,7 @@ use hir_def::{ | |||
7 | expr::{BindingAnnotation, Pat, PatId, RecordFieldPat}, | 7 | expr::{BindingAnnotation, Pat, PatId, RecordFieldPat}, |
8 | path::Path, | 8 | path::Path, |
9 | type_ref::Mutability, | 9 | type_ref::Mutability, |
10 | StructFieldId, | ||
10 | }; | 11 | }; |
11 | use hir_expand::name::Name; | 12 | use hir_expand::name::Name; |
12 | use test_utils::tested_by; | 13 | use test_utils::tested_by; |
@@ -67,6 +68,11 @@ impl<'a> InferenceContext<'a> { | |||
67 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); | 68 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); |
68 | for subpat in subpats { | 69 | for subpat in subpats { |
69 | let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); | 70 | let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); |
71 | if let Some(local_id) = matching_field { | ||
72 | let field_def = StructFieldId { parent: def.unwrap(), local_id }; | ||
73 | self.result.record_field_pat_resolutions.insert(subpat.pat, field_def); | ||
74 | } | ||
75 | |||
70 | let expected_ty = | 76 | let expected_ty = |
71 | matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs)); | 77 | matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs)); |
72 | let expected_ty = self.normalize_associated_types_in(expected_ty); | 78 | let expected_ty = self.normalize_associated_types_in(expected_ty); |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a4b8d6683..279c06d65 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -863,7 +863,7 @@ pub trait TypeWalk { | |||
863 | &mut |ty, binders| { | 863 | &mut |ty, binders| { |
864 | if let &mut Ty::Bound(bound) = ty { | 864 | if let &mut Ty::Bound(bound) = ty { |
865 | if bound.debruijn >= binders { | 865 | if bound.debruijn >= binders { |
866 | *ty = substs.0[bound.index].clone(); | 866 | *ty = substs.0[bound.index].clone().shift_bound_vars(binders); |
867 | } | 867 | } |
868 | } | 868 | } |
869 | }, | 869 | }, |
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 3a4d58bf9..8498d3d96 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs | |||
@@ -12,7 +12,7 @@ use ra_db::{ | |||
12 | }; | 12 | }; |
13 | use stdx::format_to; | 13 | use stdx::format_to; |
14 | 14 | ||
15 | use crate::{db::HirDatabase, expr::ExprValidator}; | 15 | use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; |
16 | 16 | ||
17 | #[salsa::database( | 17 | #[salsa::database( |
18 | ra_db::SourceDatabaseExtStorage, | 18 | ra_db::SourceDatabaseExtStorage, |
@@ -104,10 +104,7 @@ impl TestDB { | |||
104 | panic!("Can't find module for file") | 104 | panic!("Can't find module for file") |
105 | } | 105 | } |
106 | 106 | ||
107 | // FIXME: don't duplicate this | 107 | fn diag<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { |
108 | pub fn diagnostics(&self) -> (String, u32) { | ||
109 | let mut buf = String::new(); | ||
110 | let mut count = 0; | ||
111 | let crate_graph = self.crate_graph(); | 108 | let crate_graph = self.crate_graph(); |
112 | for krate in crate_graph.iter() { | 109 | for krate in crate_graph.iter() { |
113 | let crate_def_map = self.crate_def_map(krate); | 110 | let crate_def_map = self.crate_def_map(krate); |
@@ -132,15 +129,36 @@ impl TestDB { | |||
132 | 129 | ||
133 | for f in fns { | 130 | for f in fns { |
134 | let infer = self.infer(f.into()); | 131 | let infer = self.infer(f.into()); |
135 | let mut sink = DiagnosticSink::new(|d| { | 132 | let mut sink = DiagnosticSink::new(&mut cb); |
136 | format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
137 | count += 1; | ||
138 | }); | ||
139 | infer.add_diagnostics(self, f, &mut sink); | 133 | infer.add_diagnostics(self, f, &mut sink); |
140 | let mut validator = ExprValidator::new(f, infer, &mut sink); | 134 | let mut validator = ExprValidator::new(f, infer, &mut sink); |
141 | validator.validate_body(self); | 135 | validator.validate_body(self); |
142 | } | 136 | } |
143 | } | 137 | } |
138 | } | ||
139 | |||
140 | pub fn diagnostics(&self) -> (String, u32) { | ||
141 | let mut buf = String::new(); | ||
142 | let mut count = 0; | ||
143 | self.diag(|d| { | ||
144 | format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
145 | count += 1; | ||
146 | }); | ||
147 | (buf, count) | ||
148 | } | ||
149 | |||
150 | /// Like `diagnostics`, but filtered for a single diagnostic. | ||
151 | pub fn diagnostic<D: Diagnostic>(&self) -> (String, u32) { | ||
152 | let mut buf = String::new(); | ||
153 | let mut count = 0; | ||
154 | self.diag(|d| { | ||
155 | // We want to filter diagnostics by the particular one we are testing for, to | ||
156 | // avoid surprising results in tests. | ||
157 | if d.downcast_ref::<D>().is_some() { | ||
158 | format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
159 | count += 1; | ||
160 | }; | ||
161 | }); | ||
144 | (buf, count) | 162 | (buf, count) |
145 | } | 163 | } |
146 | } | 164 | } |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 81fc0f63a..846005baa 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -18,16 +18,19 @@ use hir_def::{ | |||
18 | nameres::CrateDefMap, | 18 | nameres::CrateDefMap, |
19 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 19 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
20 | }; | 20 | }; |
21 | use hir_expand::InFile; | 21 | use hir_expand::{db::AstDatabase, InFile}; |
22 | use insta::assert_snapshot; | 22 | use insta::assert_snapshot; |
23 | use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase}; | 23 | use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase}; |
24 | use ra_syntax::{ | 24 | use ra_syntax::{ |
25 | algo, | 25 | algo, |
26 | ast::{self, AstNode}, | 26 | ast::{self, AstNode}, |
27 | SyntaxNode, | ||
27 | }; | 28 | }; |
28 | use stdx::format_to; | 29 | use stdx::format_to; |
29 | 30 | ||
30 | use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResult}; | 31 | use crate::{ |
32 | db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty, | ||
33 | }; | ||
31 | 34 | ||
32 | // These tests compare the inference results for all expressions in a file | 35 | // These tests compare the inference results for all expressions in a file |
33 | // against snapshots of the expected results using insta. Use cargo-insta to | 36 | // against snapshots of the expected results using insta. Use cargo-insta to |
@@ -67,13 +70,19 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { | |||
67 | 70 | ||
68 | let mut infer_def = |inference_result: Arc<InferenceResult>, | 71 | let mut infer_def = |inference_result: Arc<InferenceResult>, |
69 | body_source_map: Arc<BodySourceMap>| { | 72 | body_source_map: Arc<BodySourceMap>| { |
70 | let mut types = Vec::new(); | 73 | let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new(); |
71 | let mut mismatches = Vec::new(); | 74 | let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new(); |
72 | 75 | ||
73 | for (pat, ty) in inference_result.type_of_pat.iter() { | 76 | for (pat, ty) in inference_result.type_of_pat.iter() { |
74 | let syntax_ptr = match body_source_map.pat_syntax(pat) { | 77 | let syntax_ptr = match body_source_map.pat_syntax(pat) { |
75 | Ok(sp) => { | 78 | Ok(sp) => { |
76 | sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr())) | 79 | let root = db.parse_or_expand(sp.file_id).unwrap(); |
80 | sp.map(|ptr| { | ||
81 | ptr.either( | ||
82 | |it| it.to_node(&root).syntax().clone(), | ||
83 | |it| it.to_node(&root).syntax().clone(), | ||
84 | ) | ||
85 | }) | ||
77 | } | 86 | } |
78 | Err(SyntheticSyntax) => continue, | 87 | Err(SyntheticSyntax) => continue, |
79 | }; | 88 | }; |
@@ -81,29 +90,31 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { | |||
81 | } | 90 | } |
82 | 91 | ||
83 | for (expr, ty) in inference_result.type_of_expr.iter() { | 92 | for (expr, ty) in inference_result.type_of_expr.iter() { |
84 | let syntax_ptr = match body_source_map.expr_syntax(expr) { | 93 | let node = match body_source_map.expr_syntax(expr) { |
85 | Ok(sp) => sp.map(|ast| ast.syntax_node_ptr()), | 94 | Ok(sp) => { |
95 | let root = db.parse_or_expand(sp.file_id).unwrap(); | ||
96 | sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | ||
97 | } | ||
86 | Err(SyntheticSyntax) => continue, | 98 | Err(SyntheticSyntax) => continue, |
87 | }; | 99 | }; |
88 | types.push((syntax_ptr.clone(), ty)); | 100 | types.push((node.clone(), ty)); |
89 | if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { | 101 | if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { |
90 | mismatches.push((syntax_ptr, mismatch)); | 102 | mismatches.push((node, mismatch)); |
91 | } | 103 | } |
92 | } | 104 | } |
93 | 105 | ||
94 | // sort ranges for consistency | 106 | // sort ranges for consistency |
95 | types.sort_by_key(|(src_ptr, _)| { | 107 | types.sort_by_key(|(node, _)| { |
96 | (src_ptr.value.range().start(), src_ptr.value.range().end()) | 108 | let range = node.value.text_range(); |
109 | (range.start(), range.end()) | ||
97 | }); | 110 | }); |
98 | for (src_ptr, ty) in &types { | 111 | for (node, ty) in &types { |
99 | let node = src_ptr.value.to_node(&src_ptr.file_syntax(&db)); | 112 | let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) { |
100 | |||
101 | let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.clone()) { | ||
102 | (self_param.self_token().unwrap().text_range(), "self".to_string()) | 113 | (self_param.self_token().unwrap().text_range(), "self".to_string()) |
103 | } else { | 114 | } else { |
104 | (src_ptr.value.range(), node.text().to_string().replace("\n", " ")) | 115 | (node.value.text_range(), node.value.text().to_string().replace("\n", " ")) |
105 | }; | 116 | }; |
106 | let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; | 117 | let macro_prefix = if node.file_id != file_id.into() { "!" } else { "" }; |
107 | format_to!( | 118 | format_to!( |
108 | buf, | 119 | buf, |
109 | "{}{} '{}': {}\n", | 120 | "{}{} '{}': {}\n", |
@@ -114,11 +125,12 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { | |||
114 | ); | 125 | ); |
115 | } | 126 | } |
116 | if include_mismatches { | 127 | if include_mismatches { |
117 | mismatches.sort_by_key(|(src_ptr, _)| { | 128 | mismatches.sort_by_key(|(node, _)| { |
118 | (src_ptr.value.range().start(), src_ptr.value.range().end()) | 129 | let range = node.value.text_range(); |
130 | (range.start(), range.end()) | ||
119 | }); | 131 | }); |
120 | for (src_ptr, mismatch) in &mismatches { | 132 | for (src_ptr, mismatch) in &mismatches { |
121 | let range = src_ptr.value.range(); | 133 | let range = src_ptr.value.text_range(); |
122 | let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; | 134 | let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; |
123 | format_to!( | 135 | format_to!( |
124 | buf, | 136 | buf, |
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index 07cbc521a..6ea51d5d3 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs | |||
@@ -455,3 +455,29 @@ fn test() { | |||
455 | "### | 455 | "### |
456 | ); | 456 | ); |
457 | } | 457 | } |
458 | |||
459 | #[test] | ||
460 | fn infer_guard() { | ||
461 | assert_snapshot!( | ||
462 | infer(r#" | ||
463 | struct S; | ||
464 | impl S { fn foo(&self) -> bool { false } } | ||
465 | |||
466 | fn main() { | ||
467 | match S { | ||
468 | s if s.foo() => (), | ||
469 | } | ||
470 | } | ||
471 | "#), @" | ||
472 | [28; 32) 'self': &S | ||
473 | [42; 51) '{ false }': bool | ||
474 | [44; 49) 'false': bool | ||
475 | [65; 116) '{ ... } }': () | ||
476 | [71; 114) 'match ... }': () | ||
477 | [77; 78) 'S': S | ||
478 | [89; 90) 's': S | ||
479 | [94; 95) 's': S | ||
480 | [94; 101) 's.foo()': bool | ||
481 | [105; 107) '()': () | ||
482 | ") | ||
483 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 61284d672..61a6801fc 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs | |||
@@ -533,3 +533,44 @@ fn foo(b: Bar) { | |||
533 | "### | 533 | "### |
534 | ); | 534 | ); |
535 | } | 535 | } |
536 | |||
537 | #[test] | ||
538 | fn issue_4053_diesel_where_clauses() { | ||
539 | assert_snapshot!( | ||
540 | infer(r#" | ||
541 | trait BoxedDsl<DB> { | ||
542 | type Output; | ||
543 | fn internal_into_boxed(self) -> Self::Output; | ||
544 | } | ||
545 | |||
546 | struct SelectStatement<From, Select, Distinct, Where, Order, LimitOffset, GroupBy, Locking> { | ||
547 | order: Order, | ||
548 | } | ||
549 | |||
550 | trait QueryFragment<DB: Backend> {} | ||
551 | |||
552 | trait Into<T> { fn into(self) -> T; } | ||
553 | |||
554 | impl<F, S, D, W, O, LOf, DB> BoxedDsl<DB> | ||
555 | for SelectStatement<F, S, D, W, O, LOf, G> | ||
556 | where | ||
557 | O: Into<dyn QueryFragment<DB>>, | ||
558 | { | ||
559 | type Output = XXX; | ||
560 | |||
561 | fn internal_into_boxed(self) -> Self::Output { | ||
562 | self.order.into(); | ||
563 | } | ||
564 | } | ||
565 | "#), | ||
566 | @r###" | ||
567 | [66; 70) 'self': Self | ||
568 | [268; 272) 'self': Self | ||
569 | [467; 471) 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> | ||
570 | [489; 523) '{ ... }': () | ||
571 | [499; 503) 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> | ||
572 | [499; 509) 'self.order': O | ||
573 | [499; 516) 'self.o...into()': dyn QueryFragment<DB> | ||
574 | "### | ||
575 | ); | ||
576 | } | ||
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 05791a848..6bc6d474c 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs | |||
@@ -225,7 +225,7 @@ fn solution_from_chalk( | |||
225 | None => unimplemented!(), | 225 | None => unimplemented!(), |
226 | }) | 226 | }) |
227 | .collect(); | 227 | .collect(); |
228 | let result = Canonical { value, num_vars: subst.binders.len() }; | 228 | let result = Canonical { value, num_vars: subst.binders.len(&Interner) }; |
229 | SolutionVariables(result) | 229 | SolutionVariables(result) |
230 | }; | 230 | }; |
231 | match solution { | 231 | match solution { |
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index e00a82db2..1ccb7c3b4 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs | |||
@@ -4,8 +4,8 @@ use std::{fmt, sync::Arc}; | |||
4 | use log::debug; | 4 | use log::debug; |
5 | 5 | ||
6 | use chalk_ir::{ | 6 | use chalk_ir::{ |
7 | cast::Cast, fold::shift::Shift, Goal, GoalData, Parameter, PlaceholderIndex, TypeName, | 7 | cast::Cast, fold::shift::Shift, interner::HasInterner, Goal, GoalData, Parameter, |
8 | UniverseIndex, | 8 | PlaceholderIndex, TypeName, UniverseIndex, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; | 11 | use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId}; |
@@ -33,8 +33,10 @@ impl chalk_ir::interner::Interner for Interner { | |||
33 | type InternedGoals = Vec<Goal<Self>>; | 33 | type InternedGoals = Vec<Goal<Self>>; |
34 | type InternedSubstitution = Vec<Parameter<Self>>; | 34 | type InternedSubstitution = Vec<Parameter<Self>>; |
35 | type InternedProgramClause = chalk_ir::ProgramClauseData<Self>; | 35 | type InternedProgramClause = chalk_ir::ProgramClauseData<Self>; |
36 | type InternedProgramClauses = Vec<chalk_ir::ProgramClause<Self>>; | 36 | type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>; |
37 | type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>; | 37 | type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>; |
38 | type InternedParameterKinds = Vec<chalk_ir::ParameterKind<()>>; | ||
39 | type InternedCanonicalVarKinds = Vec<chalk_ir::ParameterKind<UniverseIndex>>; | ||
38 | type Identifier = TypeAliasId; | 40 | type Identifier = TypeAliasId; |
39 | type DefId = InternId; | 41 | type DefId = InternId; |
40 | 42 | ||
@@ -60,6 +62,27 @@ impl chalk_ir::interner::Interner for Interner { | |||
60 | tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt))) | 62 | tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt))) |
61 | } | 63 | } |
62 | 64 | ||
65 | fn debug_projection_ty( | ||
66 | proj: &chalk_ir::ProjectionTy<Interner>, | ||
67 | fmt: &mut fmt::Formatter<'_>, | ||
68 | ) -> Option<fmt::Result> { | ||
69 | tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt))) | ||
70 | } | ||
71 | |||
72 | fn debug_opaque_ty( | ||
73 | opaque_ty: &chalk_ir::OpaqueTy<Interner>, | ||
74 | fmt: &mut fmt::Formatter<'_>, | ||
75 | ) -> Option<fmt::Result> { | ||
76 | tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt))) | ||
77 | } | ||
78 | |||
79 | fn debug_opaque_ty_id( | ||
80 | opaque_ty_id: chalk_ir::OpaqueTyId<Self>, | ||
81 | fmt: &mut fmt::Formatter<'_>, | ||
82 | ) -> Option<fmt::Result> { | ||
83 | tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt))) | ||
84 | } | ||
85 | |||
63 | fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { | 86 | fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { |
64 | tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt))) | 87 | tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt))) |
65 | } | 88 | } |
@@ -202,15 +225,15 @@ impl chalk_ir::interner::Interner for Interner { | |||
202 | fn intern_program_clauses( | 225 | fn intern_program_clauses( |
203 | &self, | 226 | &self, |
204 | data: impl IntoIterator<Item = chalk_ir::ProgramClause<Self>>, | 227 | data: impl IntoIterator<Item = chalk_ir::ProgramClause<Self>>, |
205 | ) -> Vec<chalk_ir::ProgramClause<Self>> { | 228 | ) -> Arc<[chalk_ir::ProgramClause<Self>]> { |
206 | data.into_iter().collect() | 229 | data.into_iter().collect() |
207 | } | 230 | } |
208 | 231 | ||
209 | fn program_clauses_data<'a>( | 232 | fn program_clauses_data<'a>( |
210 | &self, | 233 | &self, |
211 | clauses: &'a Vec<chalk_ir::ProgramClause<Self>>, | 234 | clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>, |
212 | ) -> &'a [chalk_ir::ProgramClause<Self>] { | 235 | ) -> &'a [chalk_ir::ProgramClause<Self>] { |
213 | clauses | 236 | &clauses |
214 | } | 237 | } |
215 | 238 | ||
216 | fn intern_quantified_where_clauses( | 239 | fn intern_quantified_where_clauses( |
@@ -226,6 +249,34 @@ impl chalk_ir::interner::Interner for Interner { | |||
226 | ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] { | 249 | ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] { |
227 | clauses | 250 | clauses |
228 | } | 251 | } |
252 | |||
253 | fn intern_parameter_kinds( | ||
254 | &self, | ||
255 | data: impl IntoIterator<Item = chalk_ir::ParameterKind<()>>, | ||
256 | ) -> Self::InternedParameterKinds { | ||
257 | data.into_iter().collect() | ||
258 | } | ||
259 | |||
260 | fn parameter_kinds_data<'a>( | ||
261 | &self, | ||
262 | parameter_kinds: &'a Self::InternedParameterKinds, | ||
263 | ) -> &'a [chalk_ir::ParameterKind<()>] { | ||
264 | ¶meter_kinds | ||
265 | } | ||
266 | |||
267 | fn intern_canonical_var_kinds( | ||
268 | &self, | ||
269 | data: impl IntoIterator<Item = chalk_ir::ParameterKind<UniverseIndex>>, | ||
270 | ) -> Self::InternedCanonicalVarKinds { | ||
271 | data.into_iter().collect() | ||
272 | } | ||
273 | |||
274 | fn canonical_var_kinds_data<'a>( | ||
275 | &self, | ||
276 | canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, | ||
277 | ) -> &'a [chalk_ir::ParameterKind<UniverseIndex>] { | ||
278 | &canonical_var_kinds | ||
279 | } | ||
229 | } | 280 | } |
230 | 281 | ||
231 | impl chalk_ir::interner::HasInterner for Interner { | 282 | impl chalk_ir::interner::HasInterner for Interner { |
@@ -268,9 +319,12 @@ impl ToChalk for Ty { | |||
268 | Ty::Projection(proj_ty) => { | 319 | Ty::Projection(proj_ty) => { |
269 | let associated_ty_id = proj_ty.associated_ty.to_chalk(db); | 320 | let associated_ty_id = proj_ty.associated_ty.to_chalk(db); |
270 | let substitution = proj_ty.parameters.to_chalk(db); | 321 | let substitution = proj_ty.parameters.to_chalk(db); |
271 | chalk_ir::AliasTy { associated_ty_id, substitution } | 322 | chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { |
272 | .cast(&Interner) | 323 | associated_ty_id, |
273 | .intern(&Interner) | 324 | substitution, |
325 | }) | ||
326 | .cast(&Interner) | ||
327 | .intern(&Interner) | ||
274 | } | 328 | } |
275 | Ty::Placeholder(id) => { | 329 | Ty::Placeholder(id) => { |
276 | let interned_id = db.intern_type_param_id(id); | 330 | let interned_id = db.intern_type_param_id(id); |
@@ -314,16 +368,17 @@ impl ToChalk for Ty { | |||
314 | ); | 368 | ); |
315 | Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) | 369 | Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) |
316 | } | 370 | } |
317 | chalk_ir::TyData::Alias(proj) => { | 371 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => { |
318 | let associated_ty = from_chalk(db, proj.associated_ty_id); | 372 | let associated_ty = from_chalk(db, proj.associated_ty_id); |
319 | let parameters = from_chalk(db, proj.substitution); | 373 | let parameters = from_chalk(db, proj.substitution); |
320 | Ty::Projection(ProjectionTy { associated_ty, parameters }) | 374 | Ty::Projection(ProjectionTy { associated_ty, parameters }) |
321 | } | 375 | } |
376 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), | ||
322 | chalk_ir::TyData::Function(_) => unimplemented!(), | 377 | chalk_ir::TyData::Function(_) => unimplemented!(), |
323 | chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx), | 378 | chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx), |
324 | chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown, | 379 | chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown, |
325 | chalk_ir::TyData::Dyn(where_clauses) => { | 380 | chalk_ir::TyData::Dyn(where_clauses) => { |
326 | assert_eq!(where_clauses.bounds.binders.len(), 1); | 381 | assert_eq!(where_clauses.bounds.binders.len(&Interner), 1); |
327 | let predicates = where_clauses | 382 | let predicates = where_clauses |
328 | .bounds | 383 | .bounds |
329 | .skip_binders() | 384 | .skip_binders() |
@@ -404,6 +459,7 @@ impl ToChalk for TypeCtor { | |||
404 | match type_name { | 459 | match type_name { |
405 | TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), | 460 | TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), |
406 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), | 461 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), |
462 | TypeName::OpaqueType(_) => unreachable!(), | ||
407 | TypeName::Error => { | 463 | TypeName::Error => { |
408 | // this should not be reached, since we don't represent TypeName::Error with TypeCtor | 464 | // this should not be reached, since we don't represent TypeName::Error with TypeCtor |
409 | unreachable!() | 465 | unreachable!() |
@@ -460,7 +516,8 @@ impl ToChalk for GenericPredicate { | |||
460 | } | 516 | } |
461 | GenericPredicate::Projection(projection_pred) => { | 517 | GenericPredicate::Projection(projection_pred) => { |
462 | let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner); | 518 | let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner); |
463 | let alias = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner); | 519 | let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner); |
520 | let alias = chalk_ir::AliasTy::Projection(projection); | ||
464 | make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0) | 521 | make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0) |
465 | } | 522 | } |
466 | GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"), | 523 | GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"), |
@@ -481,7 +538,13 @@ impl ToChalk for GenericPredicate { | |||
481 | GenericPredicate::Implemented(from_chalk(db, tr)) | 538 | GenericPredicate::Implemented(from_chalk(db, tr)) |
482 | } | 539 | } |
483 | chalk_ir::WhereClause::AliasEq(projection_eq) => { | 540 | chalk_ir::WhereClause::AliasEq(projection_eq) => { |
484 | let projection_ty = from_chalk(db, projection_eq.alias); | 541 | let projection_ty = from_chalk( |
542 | db, | ||
543 | match projection_eq.alias { | ||
544 | chalk_ir::AliasTy::Projection(p) => p, | ||
545 | _ => unimplemented!(), | ||
546 | }, | ||
547 | ); | ||
485 | let ty = from_chalk(db, projection_eq.ty); | 548 | let ty = from_chalk(db, projection_eq.ty); |
486 | GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty }) | 549 | GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty }) |
487 | } | 550 | } |
@@ -490,10 +553,10 @@ impl ToChalk for GenericPredicate { | |||
490 | } | 553 | } |
491 | 554 | ||
492 | impl ToChalk for ProjectionTy { | 555 | impl ToChalk for ProjectionTy { |
493 | type Chalk = chalk_ir::AliasTy<Interner>; | 556 | type Chalk = chalk_ir::ProjectionTy<Interner>; |
494 | 557 | ||
495 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasTy<Interner> { | 558 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> { |
496 | chalk_ir::AliasTy { | 559 | chalk_ir::ProjectionTy { |
497 | associated_ty_id: self.associated_ty.to_chalk(db), | 560 | associated_ty_id: self.associated_ty.to_chalk(db), |
498 | substitution: self.parameters.to_chalk(db), | 561 | substitution: self.parameters.to_chalk(db), |
499 | } | 562 | } |
@@ -501,7 +564,7 @@ impl ToChalk for ProjectionTy { | |||
501 | 564 | ||
502 | fn from_chalk( | 565 | fn from_chalk( |
503 | db: &dyn HirDatabase, | 566 | db: &dyn HirDatabase, |
504 | projection_ty: chalk_ir::AliasTy<Interner>, | 567 | projection_ty: chalk_ir::ProjectionTy<Interner>, |
505 | ) -> ProjectionTy { | 568 | ) -> ProjectionTy { |
506 | ProjectionTy { | 569 | ProjectionTy { |
507 | associated_ty: from_chalk(db, projection_ty.associated_ty_id), | 570 | associated_ty: from_chalk(db, projection_ty.associated_ty_id), |
@@ -514,7 +577,10 @@ impl ToChalk for super::ProjectionPredicate { | |||
514 | type Chalk = chalk_ir::AliasEq<Interner>; | 577 | type Chalk = chalk_ir::AliasEq<Interner>; |
515 | 578 | ||
516 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> { | 579 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> { |
517 | chalk_ir::AliasEq { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) } | 580 | chalk_ir::AliasEq { |
581 | alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)), | ||
582 | ty: self.ty.to_chalk(db), | ||
583 | } | ||
518 | } | 584 | } |
519 | 585 | ||
520 | fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self { | 586 | fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self { |
@@ -540,17 +606,24 @@ impl ToChalk for Obligation { | |||
540 | impl<T> ToChalk for Canonical<T> | 606 | impl<T> ToChalk for Canonical<T> |
541 | where | 607 | where |
542 | T: ToChalk, | 608 | T: ToChalk, |
609 | T::Chalk: HasInterner<Interner = Interner>, | ||
543 | { | 610 | { |
544 | type Chalk = chalk_ir::Canonical<T::Chalk>; | 611 | type Chalk = chalk_ir::Canonical<T::Chalk>; |
545 | 612 | ||
546 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { | 613 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { |
547 | let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT); | 614 | let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT); |
548 | let value = self.value.to_chalk(db); | 615 | let value = self.value.to_chalk(db); |
549 | chalk_ir::Canonical { value, binders: vec![parameter; self.num_vars] } | 616 | chalk_ir::Canonical { |
617 | value, | ||
618 | binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]), | ||
619 | } | ||
550 | } | 620 | } |
551 | 621 | ||
552 | fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> { | 622 | fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> { |
553 | Canonical { num_vars: canonical.binders.len(), value: from_chalk(db, canonical.value) } | 623 | Canonical { |
624 | num_vars: canonical.binders.len(&Interner), | ||
625 | value: from_chalk(db, canonical.value), | ||
626 | } | ||
554 | } | 627 | } |
555 | } | 628 | } |
556 | 629 | ||
@@ -649,9 +722,15 @@ impl ToChalk for builtin::BuiltinImplAssocTyValueData { | |||
649 | } | 722 | } |
650 | } | 723 | } |
651 | 724 | ||
652 | fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> { | 725 | fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> |
726 | where | ||
727 | T: HasInterner<Interner = Interner>, | ||
728 | { | ||
653 | chalk_ir::Binders::new( | 729 | chalk_ir::Binders::new( |
654 | std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars).collect(), | 730 | chalk_ir::ParameterKinds::from( |
731 | &Interner, | ||
732 | std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars), | ||
733 | ), | ||
655 | value, | 734 | value, |
656 | ) | 735 | ) |
657 | } | 736 | } |
@@ -799,6 +878,28 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
799 | // FIXME tell Chalk about well-known traits (here and in trait_datum) | 878 | // FIXME tell Chalk about well-known traits (here and in trait_datum) |
800 | None | 879 | None |
801 | } | 880 | } |
881 | |||
882 | fn program_clauses_for_env( | ||
883 | &self, | ||
884 | environment: &chalk_ir::Environment<Interner>, | ||
885 | ) -> chalk_ir::ProgramClauses<Interner> { | ||
886 | self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) | ||
887 | } | ||
888 | |||
889 | fn opaque_ty_data( | ||
890 | &self, | ||
891 | _id: chalk_ir::OpaqueTyId<Interner>, | ||
892 | ) -> Arc<chalk_rust_ir::OpaqueTyDatum<Interner>> { | ||
893 | unimplemented!() | ||
894 | } | ||
895 | } | ||
896 | |||
897 | pub(crate) fn program_clauses_for_chalk_env_query( | ||
898 | db: &dyn HirDatabase, | ||
899 | krate: CrateId, | ||
900 | environment: chalk_ir::Environment<Interner>, | ||
901 | ) -> chalk_ir::ProgramClauses<Interner> { | ||
902 | chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment) | ||
802 | } | 903 | } |
803 | 904 | ||
804 | pub(crate) fn associated_ty_data_query( | 905 | pub(crate) fn associated_ty_data_query( |
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs index fa8e4d1ad..4867cb17e 100644 --- a/crates/ra_hir_ty/src/traits/chalk/tls.rs +++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs | |||
@@ -121,19 +121,38 @@ impl DebugContext<'_> { | |||
121 | write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) | 121 | write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) |
122 | } | 122 | } |
123 | 123 | ||
124 | pub fn debug_opaque_ty_id( | ||
125 | &self, | ||
126 | opaque_ty_id: chalk_ir::OpaqueTyId<Interner>, | ||
127 | fmt: &mut fmt::Formatter<'_>, | ||
128 | ) -> Result<(), fmt::Error> { | ||
129 | fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish() | ||
130 | } | ||
131 | |||
124 | pub fn debug_alias( | 132 | pub fn debug_alias( |
125 | &self, | 133 | &self, |
126 | alias: &AliasTy<Interner>, | 134 | alias_ty: &AliasTy<Interner>, |
135 | fmt: &mut fmt::Formatter<'_>, | ||
136 | ) -> Result<(), fmt::Error> { | ||
137 | match alias_ty { | ||
138 | AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt), | ||
139 | AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt), | ||
140 | } | ||
141 | } | ||
142 | |||
143 | pub fn debug_projection_ty( | ||
144 | &self, | ||
145 | projection_ty: &chalk_ir::ProjectionTy<Interner>, | ||
127 | fmt: &mut fmt::Formatter<'_>, | 146 | fmt: &mut fmt::Formatter<'_>, |
128 | ) -> Result<(), fmt::Error> { | 147 | ) -> Result<(), fmt::Error> { |
129 | let type_alias: TypeAliasId = from_chalk(self.0, alias.associated_ty_id); | 148 | let type_alias: TypeAliasId = from_chalk(self.0, projection_ty.associated_ty_id); |
130 | let type_alias_data = self.0.type_alias_data(type_alias); | 149 | let type_alias_data = self.0.type_alias_data(type_alias); |
131 | let trait_ = match type_alias.lookup(self.0.upcast()).container { | 150 | let trait_ = match type_alias.lookup(self.0.upcast()).container { |
132 | AssocContainerId::TraitId(t) => t, | 151 | AssocContainerId::TraitId(t) => t, |
133 | _ => panic!("associated type not in trait"), | 152 | _ => panic!("associated type not in trait"), |
134 | }; | 153 | }; |
135 | let trait_data = self.0.trait_data(trait_); | 154 | let trait_data = self.0.trait_data(trait_); |
136 | let params = alias.substitution.parameters(&Interner); | 155 | let params = projection_ty.substitution.parameters(&Interner); |
137 | write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?; | 156 | write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?; |
138 | if params.len() > 1 { | 157 | if params.len() > 1 { |
139 | write!( | 158 | write!( |
@@ -145,6 +164,14 @@ impl DebugContext<'_> { | |||
145 | write!(fmt, ">::{}", type_alias_data.name) | 164 | write!(fmt, ">::{}", type_alias_data.name) |
146 | } | 165 | } |
147 | 166 | ||
167 | pub fn debug_opaque_ty( | ||
168 | &self, | ||
169 | opaque_ty: &chalk_ir::OpaqueTy<Interner>, | ||
170 | fmt: &mut fmt::Formatter<'_>, | ||
171 | ) -> Result<(), fmt::Error> { | ||
172 | write!(fmt, "{:?}", opaque_ty.opaque_ty_id) | ||
173 | } | ||
174 | |||
148 | pub fn debug_ty( | 175 | pub fn debug_ty( |
149 | &self, | 176 | &self, |
150 | ty: &chalk_ir::Ty<Interner>, | 177 | ty: &chalk_ir::Ty<Interner>, |