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

Add TypeFlags for TyKind in chalk-ir #639

Merged
merged 3 commits into from
Dec 11, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions chalk-integration/src/test_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,10 @@ macro_rules! lifetime {
lifetime!($($b)*)
};
}

#[macro_export]
macro_rules! empty_substitution {
() => {
chalk_ir::Substitution::empty(&chalk_integration::interner::ChalkIr)
};
}
1 change: 1 addition & 0 deletions chalk-ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ edition = "2018"

[dependencies]
lazy_static = "1.4.0"
bitflags = "1.2.1"
chalk-derive = { version = "0.44.0-dev.0", path = "../chalk-derive" }
195 changes: 192 additions & 3 deletions chalk-ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use chalk_derive::{Fold, HasInterner, SuperVisit, Visit, Zip};
use std::marker::PhantomData;

pub use crate::debug::SeparatorTraitRef;

#[macro_use(bitflags)]
extern crate bitflags;
/// Uninhabited (empty) type, used in combination with `PhantomData`.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Void {}
Expand Down Expand Up @@ -411,11 +412,150 @@ pub struct Ty<I: Interner> {
interned: I::InternedType,
}

///compute type flags for Lifetime
fn compute_lifetime_flags<I: Interner>(lifetime: &Lifetime<I>, interner: &I) -> TypeFlags {
match lifetime.data(&interner) {
LifetimeData::InferenceVar(_) => {
TypeFlags::HAS_RE_INFER
| TypeFlags::HAS_FREE_LOCAL_REGIONS
| TypeFlags::HAS_FREE_REGIONS
}
LifetimeData::Placeholder(_) => {
TypeFlags::HAS_RE_PLACEHOLDER
| TypeFlags::HAS_FREE_LOCAL_REGIONS
| TypeFlags::HAS_FREE_REGIONS
}
LifetimeData::Static | LifetimeData::Empty(_) => TypeFlags::HAS_FREE_REGIONS,
LifetimeData::Phantom(_, _) => TypeFlags::empty(),
LifetimeData::BoundVar(_) => TypeFlags::HAS_RE_LATE_BOUND,
LifetimeData::Erased => TypeFlags::HAS_RE_ERASED,
}
}

/// Compute type flags for Substitution<I>
fn compute_substitution_flags<I: Interner>(
substitution: &Substitution<I>,
interner: &I,
) -> TypeFlags {
let mut flags = TypeFlags::empty();
for generic_arg in substitution.iter(&interner) {
flags |= compute_generic_arg_flags(generic_arg, &interner);
}
flags
}

/// Compute type flags for GenericArg<I>
fn compute_generic_arg_flags<I: Interner>(generic_arg: &GenericArg<I>, interner: &I) -> TypeFlags {
match generic_arg.data(&interner) {
GenericArgData::Ty(ty) => ty.data(interner).flags,
GenericArgData::Lifetime(lifetime) => compute_lifetime_flags(lifetime, interner),
GenericArgData::Const(constant) => {
let data = constant.data(&interner);
let flags = data.ty.data(interner).flags;
match data.value {
ConstValue::BoundVar(_) => flags,
ConstValue::InferenceVar(_) => {
flags | TypeFlags::HAS_CT_INFER | TypeFlags::STILL_FURTHER_SPECIALIZABLE
}
ConstValue::Placeholder(_) => {
flags | TypeFlags::HAS_CT_PLACEHOLDER | TypeFlags::STILL_FURTHER_SPECIALIZABLE
}
ConstValue::Concrete(_) => flags,
}
}
}
}

/// Compute type flags for aliases
fn compute_alias_flags<I: Interner>(alias_ty: &AliasTy<I>, interner: &I) -> TypeFlags {
match alias_ty {
AliasTy::Projection(projection_ty) => {
TypeFlags::HAS_TY_PROJECTION
| compute_substitution_flags(&(projection_ty.substitution), interner)
}
AliasTy::Opaque(opaque_ty) => {
TypeFlags::HAS_TY_OPAQUE
| compute_substitution_flags(&(opaque_ty.substitution), interner)
}
}
}

/// Compute type flags for a TyKind
fn compute_flags<I: Interner>(kind: &TyKind<I>, interner: &I) -> TypeFlags {
match kind {
TyKind::Adt(_, substitution)
| TyKind::AssociatedType(_, substitution)
| TyKind::Tuple(_, substitution)
| TyKind::Closure(_, substitution)
| TyKind::Generator(_, substitution)
| TyKind::GeneratorWitness(_, substitution)
| TyKind::FnDef(_, substitution) => compute_substitution_flags(substitution, interner),
TyKind::Scalar(_)
| TyKind::Str
| TyKind::Never
| TyKind::Foreign(_)
| TyKind::OpaqueType(_, _) => TypeFlags::empty(),
jackh726 marked this conversation as resolved.
Show resolved Hide resolved
TyKind::Error => TypeFlags::HAS_ERROR,
TyKind::Slice(ty) | TyKind::Raw(_, ty) => ty.data(interner).flags,
TyKind::Ref(_, lifetime, ty) => {
compute_lifetime_flags(lifetime, interner) | ty.data(interner).flags
}
TyKind::Array(ty, const_ty) => {
let flags = ty.data(interner).flags;
let const_data = const_ty.data(interner);
flags
| const_data.ty.data(interner).flags
| match const_data.value {
ConstValue::BoundVar(_) | ConstValue::Concrete(_) => TypeFlags::empty(),
ConstValue::InferenceVar(_) => {
TypeFlags::HAS_CT_INFER | TypeFlags::STILL_FURTHER_SPECIALIZABLE
}
ConstValue::Placeholder(_) => {
TypeFlags::HAS_CT_PLACEHOLDER | TypeFlags::STILL_FURTHER_SPECIALIZABLE
}
}
}
TyKind::Placeholder(_) => TypeFlags::HAS_TY_PLACEHOLDER,
TyKind::Dyn(dyn_ty) => {
let lifetime_flags = compute_lifetime_flags(&(dyn_ty.lifetime), &interner);
let mut dyn_flags = TypeFlags::empty();
for var_kind in dyn_ty.bounds.value.iter(&interner) {
match &(var_kind.value) {
WhereClause::Implemented(trait_ref) => {
dyn_flags |= compute_substitution_flags(&(trait_ref.substitution), interner)
}
WhereClause::AliasEq(alias_eq) => {
dyn_flags |= compute_alias_flags(&(alias_eq.alias), &interner);
dyn_flags |= alias_eq.ty.data(&interner).flags;
}
WhereClause::LifetimeOutlives(lifetime_outlives) => {
dyn_flags |= compute_lifetime_flags(&(lifetime_outlives.a), &interner)
| compute_lifetime_flags(&(lifetime_outlives.b), &interner);
}
WhereClause::TypeOutlives(type_outlives) => {
dyn_flags |= type_outlives.ty.data(&interner).flags;
dyn_flags |= compute_lifetime_flags(&(type_outlives.lifetime), &interner);
}
}
}
lifetime_flags | dyn_flags
}
TyKind::Alias(alias_ty) => compute_alias_flags(&alias_ty, &interner),
TyKind::BoundVar(_) => TypeFlags::empty(),
TyKind::InferenceVar(_, _) => TypeFlags::HAS_TY_INFER,
TyKind::Function(fn_pointer) => {
compute_substitution_flags(&fn_pointer.substitution.0, interner)
}
}
}

impl<I: Interner> Ty<I> {
/// Creates a type from `TyKind`.
pub fn new(interner: &I, data: impl CastTo<TyKind<I>>) -> Self {
let ty_kind = data.cast(&interner);
let data = TyData {
kind: data.cast(interner),
flags: compute_flags(&ty_kind, &interner),
Copy link
Contributor

Choose a reason for hiding this comment

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

This is probably a large enough change to be in another PR, but I would expect the interner to compute the flags, so that in the case it is actually interning it only has to compute flags when it's allocating.

kind: ty_kind,
};
Ty {
interned: I::intern_ty(interner, data),
Expand Down Expand Up @@ -530,8 +670,57 @@ impl<I: Interner> Ty<I> {
pub struct TyData<I: Interner> {
/// The kind
pub kind: TyKind<I>,
/// Type flags
pub flags: TypeFlags,
}

bitflags! {
/// Contains flags indicating various properties of a Ty
pub struct TypeFlags : u16 {
/// Does the type contain an InferenceVar
const HAS_TY_INFER = 1;
/// Does the type contain a lifetime with an InferenceVar
const HAS_RE_INFER = 1 << 1;
/// Does the type contain a ConstValue with an InferenceVar
const HAS_CT_INFER = 1 << 2;
/// Does the type contain a Placeholder TyKind
const HAS_TY_PLACEHOLDER = 1 << 3;
/// Does the type contain a lifetime with a Placeholder
const HAS_RE_PLACEHOLDER = 1 << 4;
/// Does the type contain a ConstValue Placeholder
const HAS_CT_PLACEHOLDER = 1 << 5;
/// True when the type has free lifetimes related to a local context
const HAS_FREE_LOCAL_REGIONS = 1 << 6;
/// Does the type contain a projection of an associated type
const HAS_TY_PROJECTION = 1 << 7;
/// Does the type contain an opaque type
const HAS_TY_OPAQUE = 1 << 8;
/// Does the type contain an unevaluated const projection
const HAS_CT_PROJECTION = 1 << 9;
/// Does the type contain an error
const HAS_ERROR = 1 << 10;
/// Does the type contain any free lifetimes
const HAS_FREE_REGIONS = 1 << 11;
/// True when the type contains lifetimes that will be substituted when function is called
const HAS_RE_LATE_BOUND = 1 << 12;
/// True when the type contains an erased lifetime
const HAS_RE_ERASED = 1 << 13;
/// Does the type contain placeholders or inference variables that could be replaced later
const STILL_FURTHER_SPECIALIZABLE = 1 << 14;

/// True when the type contains free names local to a particular context
const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_INFER.bits
| TypeFlags::HAS_CT_INFER.bits
| TypeFlags::HAS_TY_PLACEHOLDER.bits
| TypeFlags::HAS_CT_PLACEHOLDER.bits
| TypeFlags::HAS_FREE_LOCAL_REGIONS.bits;

/// Does the type contain any form of projection
const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits
| TypeFlags::HAS_TY_OPAQUE.bits
| TypeFlags::HAS_CT_PROJECTION.bits;
}
}

/// Type data, which holds the actual type information.
#[derive(Clone, PartialEq, Eq, Hash, HasInterner)]
pub enum TyKind<I: Interner> {
Expand Down
1 change: 1 addition & 0 deletions tests/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ mod slices;
mod string;
mod subtype;
mod tuples;
mod type_flags;
mod unify;
mod unpin;
mod unsize;
Expand Down
124 changes: 124 additions & 0 deletions tests/test/type_flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use chalk_integration::interner::ChalkIr;
use chalk_integration::{empty_substitution, lifetime, ty};
jackh726 marked this conversation as resolved.
Show resolved Hide resolved
use chalk_ir::cast::Cast;
use chalk_ir::{PlaceholderIndex, TyKind, TypeFlags, UniverseIndex};

#[test]
fn placeholder_ty_flags_correct() {
let placeholder_ty = ty!(placeholder 0);
assert_eq!(
placeholder_ty.data(&ChalkIr).flags,
TypeFlags::HAS_TY_PLACEHOLDER
);
}

#[test]
fn opaque_ty_flags_correct() {
let opaque_ty = TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
opaque_ty_id: chalk_ir::OpaqueTyId {
0: chalk_integration::interner::RawId { index: 0 },
},
substitution: chalk_ir::Substitution::from_iter(
&ChalkIr,
Some(
chalk_ir::ConstData {
ty: TyKind::Placeholder(PlaceholderIndex {
ui: chalk_ir::UniverseIndex::ROOT,
idx: 0,
})
.intern(&ChalkIr),
value: chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(0)),
}
.intern(&ChalkIr)
.cast(&ChalkIr),
),
),
}))
.intern(&ChalkIr);
assert_eq!(
opaque_ty.data(&ChalkIr).flags,
TypeFlags::HAS_TY_OPAQUE
| TypeFlags::HAS_CT_INFER
| TypeFlags::STILL_FURTHER_SPECIALIZABLE
| TypeFlags::HAS_TY_PLACEHOLDER
);
}

#[test]
fn dyn_ty_flags_correct() {
let internal_ty = TyKind::Scalar(chalk_ir::Scalar::Bool).intern(&ChalkIr);
let projection_ty = chalk_ir::ProjectionTy {
associated_ty_id: chalk_ir::AssocTypeId {
0: chalk_integration::interner::RawId { index: 0 },
},
substitution: empty_substitution!(),
};
let bounds = chalk_ir::Binders::<chalk_ir::QuantifiedWhereClauses<ChalkIr>>::empty(
&ChalkIr,
chalk_ir::QuantifiedWhereClauses::from_iter(
&ChalkIr,
vec![chalk_ir::Binders::<chalk_ir::WhereClause<ChalkIr>>::empty(
&ChalkIr,
chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq {
ty: internal_ty,
alias: chalk_ir::AliasTy::Projection(projection_ty),
}),
)],
),
);
let dyn_ty = chalk_ir::DynTy {
lifetime: lifetime!(placeholder 5),
bounds,
};
let ty = TyKind::Dyn(dyn_ty).intern(&ChalkIr);
assert_eq!(
ty.data(&ChalkIr).flags,
TypeFlags::HAS_TY_PROJECTION
| TypeFlags::HAS_RE_PLACEHOLDER
| TypeFlags::HAS_FREE_LOCAL_REGIONS
| TypeFlags::HAS_FREE_REGIONS
);
}

#[test]
fn flagless_ty_has_no_flags() {
let ty = TyKind::Str.intern(&ChalkIr);
assert_eq!(ty.data(&ChalkIr).flags, TypeFlags::empty());

let fn_ty = TyKind::Function(chalk_ir::FnPointer {
num_binders: 0,
substitution: chalk_ir::FnSubst(empty_substitution!()),
sig: chalk_ir::FnSig {
abi: chalk_integration::interner::ChalkFnAbi::Rust,
safety: chalk_ir::Safety::Safe,
variadic: false,
},
})
.intern(&ChalkIr);
assert_eq!(fn_ty.data(&ChalkIr).flags, TypeFlags::empty());
}

#[test]
fn static_and_bound_lifetimes() {
let substitutions = chalk_ir::Substitution::from_iter(
&ChalkIr,
vec![
chalk_ir::GenericArgData::Lifetime(chalk_ir::LifetimeData::Static.intern(&ChalkIr))
.intern(&ChalkIr),
chalk_ir::GenericArgData::Lifetime(lifetime!(bound 5)).intern(&ChalkIr),
],
);

let ty = TyKind::Adt(
chalk_ir::AdtId {
0: chalk_integration::interner::RawId { index: 0 },
},
substitutions,
)
.intern(&ChalkIr);

assert_eq!(
ty.data(&ChalkIr).flags,
TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_LATE_BOUND
);
}