Auto merge of #117582 - compiler-errors:uplift-canonical-var, r=jackh726

Uplift `CanonicalVarInfo` and friends into `rustc_type_ir`

Depends on #117580 and #117578

Uplift `CanonicalVarInfo` and friends into `rustc_type_ir` so they can be consumed by an interner-agnostic `Canonicalizer` implementation for the new trait solver ❤️

r? `@ghost`
This commit is contained in:
bors 2023-11-22 02:43:23 +00:00
commit 739d556826
6 changed files with 322 additions and 165 deletions

View File

@ -23,6 +23,8 @@
use rustc_macros::HashStable;
use rustc_type_ir::Canonical as IrCanonical;
use rustc_type_ir::CanonicalVarInfo as IrCanonicalVarInfo;
pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind};
use smallvec::SmallVec;
use std::ops::Index;
@ -33,6 +35,8 @@
pub type Canonical<'tcx, V> = IrCanonical<TyCtxt<'tcx>, V>;
pub type CanonicalVarInfo<'tcx> = IrCanonicalVarInfo<TyCtxt<'tcx>>;
pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;
impl<'tcx> ty::TypeFoldable<TyCtxt<'tcx>> for CanonicalVarInfos<'tcx> {
@ -138,158 +142,6 @@ fn default() -> Self {
}
}
/// Information about a canonical variable that is included with the
/// canonical value. This is sufficient information for code to create
/// a copy of the canonical value in some other inference context,
/// with fresh inference variables replacing the canonical values.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable, HashStable)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct CanonicalVarInfo<'tcx> {
pub kind: CanonicalVarKind<'tcx>,
}
impl<'tcx> CanonicalVarInfo<'tcx> {
pub fn universe(&self) -> ty::UniverseIndex {
self.kind.universe()
}
#[must_use]
pub fn with_updated_universe(self, ui: ty::UniverseIndex) -> CanonicalVarInfo<'tcx> {
CanonicalVarInfo { kind: self.kind.with_updated_universe(ui) }
}
pub fn is_existential(&self) -> bool {
match self.kind {
CanonicalVarKind::Ty(_) => true,
CanonicalVarKind::PlaceholderTy(_) => false,
CanonicalVarKind::Region(_) => true,
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(..) => true,
CanonicalVarKind::PlaceholderConst(_, _) => false,
CanonicalVarKind::Effect => true,
}
}
pub fn is_region(&self) -> bool {
match self.kind {
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true,
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_, _)
| CanonicalVarKind::PlaceholderConst(_, _)
| CanonicalVarKind::Effect => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self.kind {
CanonicalVarKind::Ty(_)
| CanonicalVarKind::Region(_)
| CanonicalVarKind::Const(_, _)
| CanonicalVarKind::Effect => bug!("expected placeholder: {self:?}"),
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.bound.var.as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.bound.var.as_usize(),
CanonicalVarKind::PlaceholderConst(placeholder, _) => placeholder.bound.as_usize(),
}
}
}
/// Describes the "kind" of the canonical variable. This is a "kind"
/// in the type-theory sense of the term -- i.e., a "meta" type system
/// that analyzes type-like values.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable, HashStable)]
#[derive(TypeFoldable, TypeVisitable)]
pub enum CanonicalVarKind<'tcx> {
/// Some kind of type inference variable.
Ty(CanonicalTyVarKind),
/// A "placeholder" that represents "any type".
PlaceholderTy(ty::PlaceholderType),
/// Region variable `'?R`.
Region(ty::UniverseIndex),
/// A "placeholder" that represents "any region". Created when you
/// are solving a goal like `for<'a> T: Foo<'a>` to represent the
/// bound region `'a`.
PlaceholderRegion(ty::PlaceholderRegion),
/// Some kind of const inference variable.
Const(ty::UniverseIndex, Ty<'tcx>),
/// Effect variable `'?E`.
Effect,
/// A "placeholder" that represents "any const".
PlaceholderConst(ty::PlaceholderConst, Ty<'tcx>),
}
impl<'tcx> CanonicalVarKind<'tcx> {
pub fn universe(self) -> ty::UniverseIndex {
match self {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => ui,
CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => {
ty::UniverseIndex::ROOT
}
CanonicalVarKind::Effect => ty::UniverseIndex::ROOT,
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe,
CanonicalVarKind::Region(ui) => ui,
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe,
CanonicalVarKind::Const(ui, _) => ui,
CanonicalVarKind::PlaceholderConst(placeholder, _) => placeholder.universe,
}
}
/// Replaces the universe of this canonical variable with `ui`.
///
/// In case this is a float or int variable, this causes an ICE if
/// the updated universe is not the root.
pub fn with_updated_universe(self, ui: ty::UniverseIndex) -> CanonicalVarKind<'tcx> {
match self {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
}
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
| CanonicalVarKind::Effect => {
assert_eq!(ui, ty::UniverseIndex::ROOT);
self
}
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: ui, ..placeholder })
}
CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui),
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(ty::Placeholder { universe: ui, ..placeholder })
}
CanonicalVarKind::Const(_, ty) => CanonicalVarKind::Const(ui, ty),
CanonicalVarKind::PlaceholderConst(placeholder, ty) => {
CanonicalVarKind::PlaceholderConst(
ty::Placeholder { universe: ui, ..placeholder },
ty,
)
}
}
}
}
/// Rust actually has more than one category of type variables;
/// notably, the type variables we create for literals (e.g., 22 or
/// 22.) can only be instantiated with integral/float types (e.g.,
/// usize or f32). In order to faithfully reproduce a type, we need to
/// know what set of types a given type variable can be unified with.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable, HashStable)]
pub enum CanonicalTyVarKind {
/// General type variable `?T` that can be unified with arbitrary types.
General(ty::UniverseIndex),
/// Integral type variable `?I` (that can only be unified with integral types).
Int,
/// Floating-point type variable `?F` (that can only be unified with float types).
Float,
}
/// After we execute a query with a canonicalized key, we get back a
/// `Canonical<QueryResponse<..>>`. You can use
/// `instantiate_query_result` to access the data in this result.
@ -366,7 +218,6 @@ pub fn is_proven(&self) -> bool {
TrivialTypeTraversalImpls! {
crate::infer::canonical::Certainty,
crate::infer::canonical::CanonicalTyVarKind,
}
impl<'tcx> CanonicalVarValues<'tcx> {

View File

@ -94,7 +94,7 @@
pub use self::rvalue_scopes::RvalueScopes;
pub use self::sty::BoundRegionKind::*;
pub use self::sty::{
AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar,
AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind,
BoundVariableKind, CanonicalPolyFnSig, ClauseKind, ClosureArgs, ClosureArgsParts, ConstKind,
CoroutineArgs, CoroutineArgsParts, EarlyParamRegion, ExistentialPredicate,
ExistentialProjection, ExistentialTraitRef, FnSig, GenSig, InlineConstArgs,
@ -1517,8 +1517,36 @@ pub struct Placeholder<T> {
pub type PlaceholderRegion = Placeholder<BoundRegion>;
impl rustc_type_ir::Placeholder for PlaceholderRegion {
fn universe(&self) -> UniverseIndex {
self.universe
}
fn var(&self) -> BoundVar {
self.bound.var
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
Placeholder { universe: ui, ..self }
}
}
pub type PlaceholderType = Placeholder<BoundTy>;
impl rustc_type_ir::Placeholder for PlaceholderType {
fn universe(&self) -> UniverseIndex {
self.universe
}
fn var(&self) -> BoundVar {
self.bound.var
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
Placeholder { universe: ui, ..self }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
#[derive(TyEncodable, TyDecodable, PartialOrd, Ord)]
pub struct BoundConst<'tcx> {
@ -1528,6 +1556,20 @@ pub struct BoundConst<'tcx> {
pub type PlaceholderConst = Placeholder<BoundVar>;
impl rustc_type_ir::Placeholder for PlaceholderConst {
fn universe(&self) -> UniverseIndex {
self.universe
}
fn var(&self) -> BoundVar {
self.bound
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
Placeholder { universe: ui, ..self }
}
}
/// When type checking, we use the `ParamEnv` to track
/// details about the set of where-clauses that are in scope at this
/// particular point.

View File

@ -32,6 +32,7 @@
use std::ops::{ControlFlow, Deref, Range};
use ty::util::IntTypeExt;
use rustc_type_ir::BoundVar;
use rustc_type_ir::ClauseKind as IrClauseKind;
use rustc_type_ir::CollectAndApply;
use rustc_type_ir::ConstKind as IrConstKind;
@ -1621,12 +1622,6 @@ fn index(self) -> usize {
}
}
rustc_index::newtype_index! {
#[derive(HashStable)]
#[debug_format = "{}"]
pub struct BoundVar {}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable)]
pub struct BoundTy {

View File

@ -4,7 +4,7 @@
use crate::fold::{FallibleTypeFolder, TypeFoldable};
use crate::visit::{TypeVisitable, TypeVisitor};
use crate::{Interner, UniverseIndex};
use crate::{Interner, Placeholder, UniverseIndex};
/// A "canonicalized" type `V` is one where all free inference
/// variables have been rewritten to "canonical vars". These are
@ -113,3 +113,257 @@ fn visit_with<F: TypeVisitor<I>>(&self, folder: &mut F) -> ControlFlow<F::BreakT
self.variables.visit_with(folder)
}
}
/// Information about a canonical variable that is included with the
/// canonical value. This is sufficient information for code to create
/// a copy of the canonical value in some other inference context,
/// with fresh inference variables replacing the canonical values.
#[derive(derivative::Derivative)]
#[derivative(
Clone(bound = ""),
Hash(bound = ""),
Copy(bound = "CanonicalVarKind<I>: Copy"),
Debug(bound = "")
)]
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
pub struct CanonicalVarInfo<I: Interner> {
pub kind: CanonicalVarKind<I>,
}
impl<I: Interner> PartialEq for CanonicalVarInfo<I> {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind
}
}
impl<I: Interner> Eq for CanonicalVarInfo<I> {}
impl<I: Interner> TypeVisitable<I> for CanonicalVarInfo<I>
where
I::Ty: TypeVisitable<I>,
{
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.kind.visit_with(visitor)
}
}
impl<I: Interner> TypeFoldable<I> for CanonicalVarInfo<I>
where
I::Ty: TypeFoldable<I>,
{
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
Ok(CanonicalVarInfo { kind: self.kind.try_fold_with(folder)? })
}
}
impl<I: Interner> CanonicalVarInfo<I> {
pub fn universe(&self) -> UniverseIndex {
self.kind.universe()
}
#[must_use]
pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarInfo<I> {
CanonicalVarInfo { kind: self.kind.with_updated_universe(ui) }
}
pub fn is_existential(&self) -> bool {
match self.kind {
CanonicalVarKind::Ty(_) => true,
CanonicalVarKind::PlaceholderTy(_) => false,
CanonicalVarKind::Region(_) => true,
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(..) => true,
CanonicalVarKind::PlaceholderConst(_, _) => false,
CanonicalVarKind::Effect => true,
}
}
pub fn is_region(&self) -> bool {
match self.kind {
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true,
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_, _)
| CanonicalVarKind::PlaceholderConst(_, _)
| CanonicalVarKind::Effect => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self.kind {
CanonicalVarKind::Ty(_)
| CanonicalVarKind::Region(_)
| CanonicalVarKind::Const(_, _)
| CanonicalVarKind::Effect => panic!("expected placeholder: {self:?}"),
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderConst(placeholder, _) => placeholder.var().as_usize(),
}
}
}
/// Describes the "kind" of the canonical variable. This is a "kind"
/// in the type-theory sense of the term -- i.e., a "meta" type system
/// that analyzes type-like values.
#[derive(derivative::Derivative)]
#[derivative(Clone(bound = ""), Hash(bound = ""), Debug(bound = ""))]
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
pub enum CanonicalVarKind<I: Interner> {
/// Some kind of type inference variable.
Ty(CanonicalTyVarKind),
/// A "placeholder" that represents "any type".
PlaceholderTy(I::PlaceholderTy),
/// Region variable `'?R`.
Region(UniverseIndex),
/// A "placeholder" that represents "any region". Created when you
/// are solving a goal like `for<'a> T: Foo<'a>` to represent the
/// bound region `'a`.
PlaceholderRegion(I::PlaceholderRegion),
/// Some kind of const inference variable.
Const(UniverseIndex, I::Ty),
/// Effect variable `'?E`.
Effect,
/// A "placeholder" that represents "any const".
PlaceholderConst(I::PlaceholderConst, I::Ty),
}
impl<I: Interner> Copy for CanonicalVarKind<I>
where
I::PlaceholderTy: Copy,
I::PlaceholderRegion: Copy,
I::PlaceholderConst: Copy,
I::Ty: Copy,
{
}
impl<I: Interner> PartialEq for CanonicalVarKind<I> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Ty(l0), Self::Ty(r0)) => l0 == r0,
(Self::PlaceholderTy(l0), Self::PlaceholderTy(r0)) => l0 == r0,
(Self::Region(l0), Self::Region(r0)) => l0 == r0,
(Self::PlaceholderRegion(l0), Self::PlaceholderRegion(r0)) => l0 == r0,
(Self::Const(l0, l1), Self::Const(r0, r1)) => l0 == r0 && l1 == r1,
(Self::PlaceholderConst(l0, l1), Self::PlaceholderConst(r0, r1)) => {
l0 == r0 && l1 == r1
}
_ => std::mem::discriminant(self) == std::mem::discriminant(other),
}
}
}
impl<I: Interner> Eq for CanonicalVarKind<I> {}
impl<I: Interner> TypeVisitable<I> for CanonicalVarKind<I>
where
I::Ty: TypeVisitable<I>,
{
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
match self {
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Region(_)
| CanonicalVarKind::PlaceholderRegion(_)
| CanonicalVarKind::Effect => ControlFlow::Continue(()),
CanonicalVarKind::Const(_, ty) | CanonicalVarKind::PlaceholderConst(_, ty) => {
ty.visit_with(visitor)
}
}
}
}
impl<I: Interner> TypeFoldable<I> for CanonicalVarKind<I>
where
I::Ty: TypeFoldable<I>,
{
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
Ok(match self {
CanonicalVarKind::Ty(kind) => CanonicalVarKind::Ty(kind),
CanonicalVarKind::Region(kind) => CanonicalVarKind::Region(kind),
CanonicalVarKind::Const(kind, ty) => {
CanonicalVarKind::Const(kind, ty.try_fold_with(folder)?)
}
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(placeholder)
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(placeholder)
}
CanonicalVarKind::PlaceholderConst(placeholder, ty) => {
CanonicalVarKind::PlaceholderConst(placeholder, ty.try_fold_with(folder)?)
}
CanonicalVarKind::Effect => CanonicalVarKind::Effect,
})
}
}
impl<I: Interner> CanonicalVarKind<I> {
pub fn universe(&self) -> UniverseIndex {
match self {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => *ui,
CanonicalVarKind::Region(ui) => *ui,
CanonicalVarKind::Const(ui, _) => *ui,
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe(),
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe(),
CanonicalVarKind::PlaceholderConst(placeholder, _) => placeholder.universe(),
CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => {
UniverseIndex::ROOT
}
CanonicalVarKind::Effect => UniverseIndex::ROOT,
}
}
/// Replaces the universe of this canonical variable with `ui`.
///
/// In case this is a float or int variable, this causes an ICE if
/// the updated universe is not the root.
pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarKind<I> {
match self {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
}
CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui),
CanonicalVarKind::Const(_, ty) => CanonicalVarKind::Const(ui, ty),
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(placeholder.with_updated_universe(ui))
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(placeholder.with_updated_universe(ui))
}
CanonicalVarKind::PlaceholderConst(placeholder, ty) => {
CanonicalVarKind::PlaceholderConst(placeholder.with_updated_universe(ui), ty)
}
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
| CanonicalVarKind::Effect => {
assert_eq!(ui, UniverseIndex::ROOT);
self
}
}
}
}
/// Rust actually has more than one category of type variables;
/// notably, the type variables we create for literals (e.g., 22 or
/// 22.) can only be instantiated with integral/float types (e.g.,
/// usize or f32). In order to faithfully reproduce a type, we need to
/// know what set of types a given type variable can be unified with.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
pub enum CanonicalTyVarKind {
/// General type variable `?T` that can be unified with arbitrary types.
General(UniverseIndex),
/// Integral type variable `?I` (that can only be unified with integral types).
Int,
/// Floating-point type variable `?F` (that can only be unified with float types).
Float,
}

View File

@ -2,7 +2,7 @@
use std::fmt::Debug;
use std::hash::Hash;
use crate::{DebugWithInfcx, Mutability};
use crate::{BoundVar, DebugWithInfcx, Mutability, UniverseIndex};
pub trait Interner: Sized {
type DefId: Clone + Debug + Hash + Ord;
@ -26,7 +26,7 @@ pub trait Interner: Sized {
type AliasTy: Clone + DebugWithInfcx<Self> + Hash + Ord;
type ParamTy: Clone + Debug + Hash + Ord;
type BoundTy: Clone + Debug + Hash + Ord;
type PlaceholderTy: Clone + Debug + Hash + Ord;
type PlaceholderTy: Clone + Debug + Hash + Ord + Placeholder;
// Things stored inside of tys
type ErrorGuaranteed: Clone + Debug + Hash + Ord;
@ -37,7 +37,7 @@ pub trait Interner: Sized {
// Kinds of consts
type Const: Clone + DebugWithInfcx<Self> + Hash + Ord;
type AliasConst: Clone + DebugWithInfcx<Self> + Hash + Ord;
type PlaceholderConst: Clone + Debug + Hash + Ord;
type PlaceholderConst: Clone + Debug + Hash + Ord + Placeholder;
type ParamConst: Clone + Debug + Hash + Ord;
type BoundConst: Clone + Debug + Hash + Ord;
type ValueConst: Clone + Debug + Hash + Ord;
@ -49,7 +49,7 @@ pub trait Interner: Sized {
type BoundRegion: Clone + Debug + Hash + Ord;
type LateParamRegion: Clone + Debug + Hash + Ord;
type InferRegion: Clone + DebugWithInfcx<Self> + Hash + Ord;
type PlaceholderRegion: Clone + Debug + Hash + Ord;
type PlaceholderRegion: Clone + Debug + Hash + Ord + Placeholder;
// Predicates
type Predicate: Clone + Debug + Hash + Eq;
@ -64,6 +64,14 @@ pub trait Interner: Sized {
fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Mutability);
}
/// Common capabilities of placeholder kinds
pub trait Placeholder {
fn universe(&self) -> UniverseIndex;
fn var(&self) -> BoundVar;
fn with_updated_universe(self, ui: UniverseIndex) -> Self;
}
/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
/// that produces `T` items. You could combine them with
/// `f(&iter.collect::<Vec<_>>())`, but this requires allocating memory for the

View File

@ -332,3 +332,10 @@ pub fn cannot_name(self, other: UniverseIndex) -> bool {
self.private < other.private
}
}
rustc_index::newtype_index! {
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
#[debug_format = "{}"]
#[gate_rustc_only]
pub struct BoundVar {}
}