@@ -1683,34 +1683,59 @@ pub struct BoundConst<'tcx> {
1683
1683
1684
1684
pub type PlaceholderConst < ' tcx > = Placeholder < BoundConst < ' tcx > > ;
1685
1685
1686
- /// A `DefId` which is potentially bundled with its corresponding generic parameter
1687
- /// in case `did` is a const argument .
1686
+ /// A `DefId` which, in case it is a const argument, is potentially bundled with
1687
+ /// the `DefId` of the generic parameter it instantiates .
1688
1688
///
1689
- /// This is used to prevent cycle errors during typeck
1690
- /// as `type_of(const_arg)` depends on `typeck(owning_body)`
1691
- /// which once again requires the type of its generic arguments.
1692
- ///
1693
- /// Luckily we only need to deal with const arguments once we
1694
- /// know their corresponding parameters. We (ab)use this by
1695
- /// calling `type_of(param_did)` for these arguments.
1689
+ /// This is used to avoid calls to `type_of` for const arguments during typeck
1690
+ /// which cause cycle errors.
1696
1691
///
1697
1692
/// ```rust
1698
1693
/// #![feature(const_generics)]
1699
1694
///
1700
1695
/// struct A;
1701
1696
/// impl A {
1702
- /// fn foo<const N: usize>(&self) -> usize { N }
1697
+ /// fn foo<const N: usize>(&self) -> [u8; N] { [0; N] }
1698
+ /// // ^ const parameter
1703
1699
/// }
1704
1700
/// struct B;
1705
1701
/// impl B {
1706
- /// fn foo<const N: u8>(&self) -> usize { 42 }
1702
+ /// fn foo<const M: u8>(&self) -> usize { 42 }
1703
+ /// // ^ const parameter
1707
1704
/// }
1708
1705
///
1709
1706
/// fn main() {
1710
1707
/// let a = A;
1711
- /// a.foo::<7>();
1708
+ /// let _b = a.foo::<{ 3 + 7 }>();
1709
+ /// // ^^^^^^^^^ const argument
1712
1710
/// }
1713
1711
/// ```
1712
+ ///
1713
+ /// Let's look at the call `a.foo::<{ 3 + 7 }>()` here. We do not know
1714
+ /// which `foo` is used until we know the type of `a`.
1715
+ ///
1716
+ /// We only know the type of `a` once we are inside of `typeck(main)`.
1717
+ /// We also end up normalizing the type of `_b` during `typeck(main)` which
1718
+ /// requires us to evaluate the const argument.
1719
+ ///
1720
+ /// To evaluate that const argument we need to know its type,
1721
+ /// which we would get using `type_of(const_arg)`. This requires us to
1722
+ /// resolve `foo` as it can be either `usize` or `u8` in this example.
1723
+ /// However, resolving `foo` once again requires `typeck(main)` to get the type of `a`,
1724
+ /// which results in a cycle.
1725
+ ///
1726
+ /// In short we must not call `type_of(const_arg)` during `typeck(main)`.
1727
+ ///
1728
+ /// When first creating the `ty::Const` of the const argument inside of `typeck` we have
1729
+ /// already resolved `foo` so we know which const parameter this argument instantiates.
1730
+ /// This means that we also know the expected result of `type_of(const_arg)` even if we
1731
+ /// aren't allowed to call that query: it is equal to `type_of(const_param)` which is
1732
+ /// trivial to compute.
1733
+ ///
1734
+ /// If we now want to use that constant in a place which potentionally needs its type
1735
+ /// we also pass the type of its `const_param`. This is the point of `WithOptConstParam`,
1736
+ /// except that instead of a `Ty` we bundle the `DefId` of the const parameter.
1737
+ /// Meaning that we need to use `type_of(const_param_did)` if `const_param_did` is `Some`
1738
+ /// to get the type of `did`.
1714
1739
#[ derive( Copy , Clone , Debug , TypeFoldable , Lift , TyEncodable , TyDecodable ) ]
1715
1740
#[ derive( PartialEq , Eq , PartialOrd , Ord ) ]
1716
1741
#[ derive( Hash , HashStable ) ]
@@ -1721,7 +1746,7 @@ pub struct WithOptConstParam<T> {
1721
1746
///
1722
1747
/// Note that even if `did` is a const argument, this may still be `None`.
1723
1748
/// All queries taking `WithOptConstParam` start by calling `tcx.opt_const_param_of(def.did)`
1724
- /// to potentially update `param_did` in case it `None`.
1749
+ /// to potentially update `param_did` in the case it is `None`.
1725
1750
pub const_param_did : Option < DefId > ,
1726
1751
}
1727
1752
0 commit comments