From bf7d38c6f4bd6268092aa94b22950f3779698fe7 Mon Sep 17 00:00:00 2001 From: Mango Date: Wed, 28 Oct 2020 20:24:29 -0500 Subject: [PATCH] Add TypeFlags for TyKind in chalk-ir --- Cargo.lock | 1 + chalk-integration/src/test_macros.rs | 8 ++ chalk-ir/Cargo.toml | 1 + chalk-ir/src/lib.rs | 166 ++++++++++++++++++++++++++- tests/test/mod.rs | 1 + tests/test/type_flags.rs | 102 ++++++++++++++++ 6 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 tests/test/type_flags.rs diff --git a/Cargo.lock b/Cargo.lock index 7398bf263ab..f528c441a68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,6 +211,7 @@ dependencies = [ name = "chalk-ir" version = "0.36.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 338ec01d905..9e25138b803 100644 --- a/chalk-integration/src/test_macros.rs +++ b/chalk-integration/src/test_macros.rs @@ -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::>::new()); + }; +} \ No newline at end of file diff --git a/chalk-ir/Cargo.toml b/chalk-ir/Cargo.toml index c0658764a4b..ba459c600e0 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.36.0-dev.0", path = "../chalk-derive" } diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index d6b70e8465c..2d409b08338 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 {} @@ -327,11 +328,121 @@ 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::Phantom(_,_) + | LifetimeData::BoundVar(_) => {TypeFlags::empty()}, + } +} + +/// 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) => 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 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), @@ -446,8 +557,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 e4222fd4125..b2fb99cff5b 100644 --- a/tests/test/mod.rs +++ b/tests/test/mod.rs @@ -351,3 +351,4 @@ mod unify; mod unpin; mod unsize; mod wf_goals; +mod type_flags; \ No newline at end of file diff --git a/tests/test/type_flags.rs b/tests/test/type_flags.rs new file mode 100644 index 00000000000..f9bb566744f --- /dev/null +++ b/tests/test/type_flags.rs @@ -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> = 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::>::empty( + &chalk_integration::interner::ChalkIr, + chalk_ir::QuantifiedWhereClauses::from_iter(&chalk_integration::interner::ChalkIr, + vec!(chalk_ir::Binders::>::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()); +} \ No newline at end of file