Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uplift Canonical to rustc_type_ir #117008

Merged
merged 2 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{
self, AdtKind, CanonicalUserType, GenericParamDefKind, Ty, TyCtxt, UserType,
self, AdtKind, CanonicalUserType, GenericParamDefKind, IsIdentity, Ty, TyCtxt, UserType,
};
use rustc_middle::ty::{GenericArgKind, GenericArgsRef, UserArgs, UserSelfTy};
use rustc_session::lint;
Expand Down Expand Up @@ -207,6 +207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
debug!("fcx {}", self.tag());

// FIXME: is_identity being on `UserType` and not `Canonical<UserType>` is awkward
if !canonical_user_type_annotation.is_identity() {
self.typeck_results
.borrow_mut()
Expand Down
80 changes: 6 additions & 74 deletions compiler/rustc_middle/src/infer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,17 @@
//!
//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html

use crate::infer::MemberConstraint;
use crate::mir::ConstraintCategory;
use crate::ty::GenericArg;
use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt};
use rustc_macros::HashStable;
use rustc_type_ir::Canonical as IrCanonical;
use smallvec::SmallVec;
use std::fmt::Display;
use std::ops::Index;

/// A "canonicalized" type `V` is one where all free inference
/// variables have been rewritten to "canonical vars". These are
/// numbered starting from 0 in order of first appearance.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable)]
pub struct Canonical<'tcx, V> {
pub value: V,
pub max_universe: ty::UniverseIndex,
pub variables: CanonicalVarInfos<'tcx>,
}
use crate::infer::MemberConstraint;
use crate::mir::ConstraintCategory;
use crate::ty::GenericArg;
use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt};

impl<'tcx, V: Display> std::fmt::Display for Canonical<'tcx, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}",
self.value, self.max_universe, self.variables
)
}
}
pub type Canonical<'tcx, V> = IrCanonical<TyCtxt<'tcx>, V>;

pub type CanonicalVarInfos<'tcx> = &'tcx List<CanonicalVarInfo<'tcx>>;

Expand Down Expand Up @@ -379,56 +361,6 @@ impl<'tcx, R> QueryResponse<'tcx, R> {
}
}

impl<'tcx, R> Canonical<'tcx, QueryResponse<'tcx, R>> {
pub fn is_proven(&self) -> bool {
self.value.is_proven()
}

pub fn is_ambiguous(&self) -> bool {
!self.is_proven()
}
}

impl<'tcx, V> Canonical<'tcx, V> {
/// Allows you to map the `value` of a canonical while keeping the
/// same set of bound variables.
///
/// **WARNING:** This function is very easy to mis-use, hence the
/// name! In particular, the new value `W` must use all **the
/// same type/region variables** in **precisely the same order**
/// as the original! (The ordering is defined by the
/// `TypeFoldable` implementation of the type in question.)
///
/// An example of a **correct** use of this:
///
/// ```rust,ignore (not real code)
/// let a: Canonical<'_, T> = ...;
/// let b: Canonical<'_, (T,)> = a.unchecked_map(|v| (v, ));
/// ```
///
/// An example of an **incorrect** use of this:
///
/// ```rust,ignore (not real code)
/// let a: Canonical<'tcx, T> = ...;
/// let ty: Ty<'tcx> = ...;
/// let b: Canonical<'tcx, (T, Ty<'tcx>)> = a.unchecked_map(|v| (v, ty));
/// ```
pub fn unchecked_map<W>(self, map_op: impl FnOnce(V) -> W) -> Canonical<'tcx, W> {
let Canonical { max_universe, variables, value } = self;
Canonical { max_universe, variables, value: map_op(value) }
}

/// Allows you to map the `value` of a canonical while keeping the same set of
/// bound variables.
///
/// **WARNING:** This function is very easy to mis-use, hence the name! See
/// the comment of [Canonical::unchecked_map] for more details.
pub fn unchecked_rebind<W>(self, value: W) -> Canonical<'tcx, W> {
let Canonical { max_universe, variables, value: _ } = self;
Canonical { max_universe, variables, value }
}
}

pub type QueryOutlivesConstraint<'tcx> =
(ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>, ConstraintCategory<'tcx>);

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub mod tls;

use crate::arena::Arena;
use crate::dep_graph::{DepGraph, DepKindStruct};
use crate::infer::canonical::CanonicalVarInfo;
use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::struct_lint_level;
use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
Expand Down Expand Up @@ -88,6 +88,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {

type Binder<T> = Binder<'tcx, T>;
type TypeAndMut = TypeAndMut<'tcx>;
type CanonicalVars = CanonicalVarInfos<'tcx>;

type Ty = Ty<'tcx>;
type Tys = &'tcx List<Ty<'tcx>>;
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ pub use self::sty::{
};
pub use self::trait_def::TraitDef;
pub use self::typeck_results::{
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, TypeckResults,
UserType, UserTypeAnnotationIndex,
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity,
TypeckResults, UserType, UserTypeAnnotationIndex,
};

pub mod _match;
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,6 @@ TrivialTypeTraversalImpls! {
crate::ty::IntVarValue,
crate::ty::adjustment::PointerCoercion,
crate::ty::RegionVid,
crate::ty::UniverseIndex,
crate::ty::Variance,
::rustc_span::Span,
::rustc_span::symbol::Ident,
Expand Down
34 changes: 19 additions & 15 deletions compiler/rustc_middle/src/ty/typeck_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,10 +594,27 @@ pub struct CanonicalUserTypeAnnotation<'tcx> {
/// Canonical user type annotation.
pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>;

impl<'tcx> CanonicalUserType<'tcx> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer using an extension trait I think, UserType::is_identity seems wrong and easy to misuse

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okie

/// A user-given type annotation attached to a constant. These arise
/// from constants that are named via paths, like `Foo::<A>::new` and
/// so forth.
#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)]
#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum UserType<'tcx> {
Ty(Ty<'tcx>),

/// The canonical type is the result of `type_of(def_id)` with the
/// given substitutions applied.
TypeOf(DefId, UserArgs<'tcx>),
}

pub trait IsIdentity {
fn is_identity(&self) -> bool;
}

impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
/// Returns `true` if this represents a substitution of the form `[?0, ?1, ?2]`,
/// i.e., each thing is mapped to a canonical variable with the same index.
pub fn is_identity(&self) -> bool {
fn is_identity(&self) -> bool {
match self.value {
UserType::Ty(_) => false,
UserType::TypeOf(_, user_args) => {
Expand Down Expand Up @@ -640,19 +657,6 @@ impl<'tcx> CanonicalUserType<'tcx> {
}
}

/// A user-given type annotation attached to a constant. These arise
/// from constants that are named via paths, like `Foo::<A>::new` and
/// so forth.
#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)]
#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum UserType<'tcx> {
Ty(Ty<'tcx>),

/// The canonical type is the result of `type_of(def_id)` with the
/// given substitutions applied.
TypeOf(DefId, UserArgs<'tcx>),
}

impl<'tcx> std::fmt::Display for UserType<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
_ => unreachable!(),
}?;
// We don't expect ambiguity.
if result.is_ambiguous() {
if !result.value.is_proven() {
// Rustdoc normalizes possibly not well-formed types, so only
// treat this as a bug if we're not in rustdoc.
if !tcx.sess.opts.actually_rustdoc {
Expand Down
169 changes: 169 additions & 0 deletions compiler/rustc_type_ir/src/canonical.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use std::fmt;
use std::hash;
use std::ops::ControlFlow;

use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_serialize::{Decodable, Encodable};

use crate::fold::{FallibleTypeFolder, TypeFoldable};
use crate::visit::{TypeVisitable, TypeVisitor};
use crate::TyDecoder;
use crate::{HashStableContext, Interner, TyEncoder, UniverseIndex};

/// A "canonicalized" type `V` is one where all free inference
/// variables have been rewritten to "canonical vars". These are
/// numbered starting from 0 in order of first appearance.
pub struct Canonical<I: Interner, V> {
pub value: V,
pub max_universe: UniverseIndex,
pub variables: I::CanonicalVars,
}

impl<I: Interner, V> Canonical<I, V> {
/// Allows you to map the `value` of a canonical while keeping the
/// same set of bound variables.
///
/// **WARNING:** This function is very easy to mis-use, hence the
/// name! In particular, the new value `W` must use all **the
/// same type/region variables** in **precisely the same order**
/// as the original! (The ordering is defined by the
/// `TypeFoldable` implementation of the type in question.)
///
/// An example of a **correct** use of this:
///
/// ```rust,ignore (not real code)
/// let a: Canonical<I, T> = ...;
/// let b: Canonical<I, (T,)> = a.unchecked_map(|v| (v, ));
/// ```
///
/// An example of an **incorrect** use of this:
///
/// ```rust,ignore (not real code)
/// let a: Canonical<I, T> = ...;
/// let ty: Ty<I> = ...;
/// let b: Canonical<I, (T, Ty<I>)> = a.unchecked_map(|v| (v, ty));
/// ```
pub fn unchecked_map<W>(self, map_op: impl FnOnce(V) -> W) -> Canonical<I, W> {
let Canonical { max_universe, variables, value } = self;
Canonical { max_universe, variables, value: map_op(value) }
}

/// Allows you to map the `value` of a canonical while keeping the same set of
/// bound variables.
///
/// **WARNING:** This function is very easy to mis-use, hence the name! See
/// the comment of [Canonical::unchecked_map] for more details.
pub fn unchecked_rebind<W>(self, value: W) -> Canonical<I, W> {
let Canonical { max_universe, variables, value: _ } = self;
Canonical { max_universe, variables, value }
}
}

impl<I: Interner, V: hash::Hash> hash::Hash for Canonical<I, V> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
self.max_universe.hash(state);
self.variables.hash(state);
}
}

impl<CTX: HashStableContext, I: Interner, V: HashStable<CTX>> HashStable<CTX> for Canonical<I, V>
where
I::CanonicalVars: HashStable<CTX>,
{
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
self.value.hash_stable(hcx, hasher);
self.max_universe.hash_stable(hcx, hasher);
self.variables.hash_stable(hcx, hasher);
}
}

impl<I: Interner, V: Eq> Eq for Canonical<I, V> {}

impl<I: Interner, V: PartialEq> PartialEq for Canonical<I, V> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
&& self.max_universe == other.max_universe
&& self.variables == other.variables
}
}

impl<I: Interner, V: fmt::Display> fmt::Display for Canonical<I, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}",
self.value, self.max_universe, self.variables
)
}
}

impl<I: Interner, V: fmt::Debug> fmt::Debug for Canonical<I, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Canonical")
.field("value", &self.value)
.field("max_universe", &self.max_universe)
.field("variables", &self.variables)
.finish()
}
}

impl<I: Interner, V: Clone> Clone for Canonical<I, V> {
fn clone(&self) -> Self {
Canonical {
value: self.value.clone(),
max_universe: self.max_universe.clone(),
variables: self.variables.clone(),
}
}
}

impl<I: Interner, V: Copy> Copy for Canonical<I, V> where I::CanonicalVars: Copy {}

impl<I: Interner, V: TypeFoldable<I>> TypeFoldable<I> for Canonical<I, V>
where
I::CanonicalVars: TypeFoldable<I>,
{
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
Ok(Canonical {
value: self.value.try_fold_with(folder)?,
max_universe: self.max_universe.try_fold_with(folder)?,
variables: self.variables.try_fold_with(folder)?,
})
}
}

impl<I: Interner, V: TypeVisitable<I>> TypeVisitable<I> for Canonical<I, V>
where
I::CanonicalVars: TypeVisitable<I>,
{
fn visit_with<F: TypeVisitor<I>>(&self, folder: &mut F) -> ControlFlow<F::BreakTy> {
self.value.visit_with(folder)?;
self.max_universe.visit_with(folder)?;
self.variables.visit_with(folder)
}
}

impl<I: Interner, E: TyEncoder<I = I>, V: Encodable<E>> Encodable<E> for Canonical<I, V>
where
I::CanonicalVars: Encodable<E>,
{
fn encode(&self, s: &mut E) {
self.value.encode(s);
self.max_universe.encode(s);
self.variables.encode(s);
}
}

impl<I: Interner, D: TyDecoder<I = I>, V: Decodable<D>> Decodable<D> for Canonical<I, V>
where
I::CanonicalVars: Decodable<D>,
{
fn decode(d: &mut D) -> Self {
Canonical {
value: Decodable::decode(d),
max_universe: Decodable::decode(d),
variables: Decodable::decode(d),
}
}
}
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub trait Interner: Sized {

type Binder<T>;
type TypeAndMut: Clone + Debug + Hash + Ord;
type CanonicalVars: Clone + Debug + Hash + Eq;

// Kinds of tys
type Ty: Clone + DebugWithInfcx<Self> + Hash + Ord;
Expand Down
Loading
Loading