diff --git a/Cargo.lock b/Cargo.lock index 358e6ca58ca..8b60646fced 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,6 +211,7 @@ dependencies = [ name = "chalk-ir" version = "0.44.0-dev.0" dependencies = [ + "bitflags", "chalk-derive", "lazy_static", ] diff --git a/chalk-integration/src/test_macros.rs b/chalk-integration/src/test_macros.rs index 6febabc7134..8ac6934ec33 100644 --- a/chalk-integration/src/test_macros.rs +++ b/chalk-integration/src/test_macros.rs @@ -119,3 +119,10 @@ macro_rules! lifetime { lifetime!($($b)*) }; } + +#[macro_export] +macro_rules! empty_substitution { + () => { + chalk_ir::Substitution::empty(&chalk_integration::interner::ChalkIr) + }; +} diff --git a/chalk-ir/Cargo.toml b/chalk-ir/Cargo.toml index fd27cfd07e5..f9ec00af424 100644 --- a/chalk-ir/Cargo.toml +++ b/chalk-ir/Cargo.toml @@ -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" } diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index 59d8335a6fe..c83f13e17bd 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -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 {} @@ -411,11 +412,147 @@ pub struct Ty { interned: I::InternedType, } +///compute type flags for Lifetime +fn compute_lifetime_flags(lifetime: &Lifetime, 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 +fn compute_substitution_flags( + substitution: &Substitution, + 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 +fn compute_generic_arg_flags(generic_arg: &GenericArg, 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(alias_ty: &AliasTy, 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(kind: &TyKind, 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) + | TyKind::OpaqueType(_, substitution) => compute_substitution_flags(substitution, interner), + TyKind::Scalar(_) | TyKind::Str | TyKind::Never | TyKind::Foreign(_) => TypeFlags::empty(), + 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 Ty { /// Creates a type from `TyKind`. pub fn new(interner: &I, data: impl CastTo>) -> 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), @@ -530,8 +667,57 @@ impl Ty { pub struct TyData { /// The kind pub kind: TyKind, + /// 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 { diff --git a/tests/test/mod.rs b/tests/test/mod.rs index f8d39aaabec..0b97bfe1b45 100644 --- a/tests/test/mod.rs +++ b/tests/test/mod.rs @@ -348,6 +348,7 @@ mod slices; mod string; mod subtype; mod tuples; +mod type_flags; mod unify; mod unpin; mod unsize; diff --git a/tests/test/type_flags.rs b/tests/test/type_flags.rs new file mode 100644 index 00000000000..c568fc7306f --- /dev/null +++ b/tests/test/type_flags.rs @@ -0,0 +1,124 @@ +use chalk_integration::interner::ChalkIr; +use chalk_integration::{empty_substitution, lifetime, ty}; +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::>::empty( + &ChalkIr, + chalk_ir::QuantifiedWhereClauses::from_iter( + &ChalkIr, + vec![chalk_ir::Binders::>::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 + ); +}