Skip to content

Commit

Permalink
Add TypeFlags for TyKind in chalk-ir
Browse files Browse the repository at this point in the history
  • Loading branch information
chfont committed Oct 29, 2020
1 parent 70e77f1 commit bf7d38c
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 3 deletions.
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.

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

#[macro_export]
macro_rules! empty_substitution {
() => {
chalk_ir::Substitution::from_iter(&chalk_integration::interner::ChalkIr,
Vec::<chalk_ir::GenericArg<chalk_integration::interner::ChalkIr>>::new());
};
}
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.36.0-dev.0", path = "../chalk-derive" }
166 changes: 163 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 @@ -327,11 +328,121 @@ 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::Phantom(_,_)
| LifetimeData::BoundVar(_) => {TypeFlags::empty()},
}
}

/// 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(_) => TypeFlags::empty(),
TyKind::OpaqueType(_, substitution) => TypeFlags::HAS_TY_OPAQUE | compute_substitution_flags(substitution,interner),
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), 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),
kind: ty_kind,
};
Ty {
interned: I::intern_ty(interner, data),
Expand Down Expand Up @@ -446,8 +557,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 @@ -351,3 +351,4 @@ mod unify;
mod unpin;
mod unsize;
mod wf_goals;
mod type_flags;
102 changes: 102 additions & 0 deletions tests/test/type_flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use chalk_integration::{ty, lifetime, empty_substitution};
use chalk_ir::{TyKind, Ty, UniverseIndex, PlaceholderIndex};

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

#[test]
fn opaque_ty_flags_correct(){
let x : Vec<chalk_ir::GenericArg<chalk_integration::interner::ChalkIr>> = vec!(
chalk_ir::GenericArg::new(&chalk_integration::interner::ChalkIr, chalk_ir::GenericArgData::Const(
chalk_ir::Const::new(&chalk_integration::interner::ChalkIr, chalk_ir::ConstData{
ty : Ty::new(&chalk_integration::interner::ChalkIr,
TyKind::Placeholder(PlaceholderIndex{
ui : chalk_ir::UniverseIndex::ROOT,
idx : 0
})),
value : chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(0))
})
))
);
let opaque_ty_kind = TyKind::OpaqueType(chalk_ir::OpaqueTyId{0 : chalk_integration::interner::RawId{ index : 0 }},
chalk_ir::Substitution::from_iter(&chalk_integration::interner::ChalkIr, x));
let opaque_ty = Ty::new(&chalk_integration::interner::ChalkIr, opaque_ty_kind);
assert_eq!(opaque_ty.data(&chalk_integration::interner::ChalkIr).flags,
chalk_ir::TypeFlags::HAS_TY_OPAQUE | chalk_ir::TypeFlags::HAS_CT_INFER | chalk_ir::TypeFlags::STILL_FURTHER_SPECIALIZABLE
| chalk_ir::TypeFlags::HAS_TY_PLACEHOLDER);
}

#[test]
fn dyn_ty_flags_correct(){
let internal_ty = Ty::new(&chalk_integration::interner::ChalkIr,
chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Bool));
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<chalk_integration::interner::ChalkIr>>::empty(
&chalk_integration::interner::ChalkIr,
chalk_ir::QuantifiedWhereClauses::from_iter(&chalk_integration::interner::ChalkIr,
vec!(chalk_ir::Binders::<chalk_ir::WhereClause<chalk_integration::interner::ChalkIr>>::empty(
&chalk_integration::interner::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 : bounds
};
let ty = chalk_ir::Ty::new(&chalk_integration::interner::ChalkIr, TyKind::Dyn(dyn_ty));
assert_eq!(ty.data(&chalk_integration::interner::ChalkIr).flags,
chalk_ir::TypeFlags::HAS_TY_PROJECTION | chalk_ir::TypeFlags::HAS_RE_PLACEHOLDER |
chalk_ir::TypeFlags::HAS_FREE_LOCAL_REGIONS | chalk_ir::TypeFlags::HAS_FREE_REGIONS);
}

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

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

#[test]
fn flagless_lifetime_contributes_no_flags(){
let substitutions = chalk_ir::Substitution::from_iter(&chalk_integration::interner::ChalkIr,
vec!(
chalk_ir::GenericArg::new(&chalk_integration::interner::ChalkIr
,chalk_ir::GenericArgData::Lifetime(chalk_ir::Lifetime::new(&chalk_integration::interner::ChalkIr, chalk_ir::LifetimeData::Static))),
chalk_ir::GenericArg::new(&chalk_integration::interner::ChalkIr
,chalk_ir::GenericArgData::Lifetime(lifetime!(bound 5)))
));

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

assert_eq!(ty.data(&chalk_integration::interner::ChalkIr).flags, chalk_ir::TypeFlags::empty());
}

0 comments on commit bf7d38c

Please sign in to comment.