Lazy evaluate consts in path_to_const

This commit is contained in:
hkalbasi 2023-05-03 18:54:30 +03:30
parent 0dd94d3b07
commit aafe9b1e06
6 changed files with 76 additions and 17 deletions

View File

@ -76,6 +76,7 @@ pub(crate) fn path_to_const(
mode: ParamLoweringMode,
args_lazy: impl FnOnce() -> Generics,
debruijn: DebruijnIndex,
expected_ty: Ty,
) -> Option<Const> {
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
Some(ValueNs::GenericParam(p)) => {
@ -100,6 +101,10 @@ pub(crate) fn path_to_const(
};
Some(ConstData { ty, value }.intern(Interner))
}
Some(ValueNs::ConstId(c)) => Some(intern_const_scalar(
ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
expected_ty,
)),
_ => None,
}
}
@ -227,9 +232,10 @@ pub(crate) fn eval_to_const(
debruijn: DebruijnIndex,
) -> Const {
let db = ctx.db;
let infer = ctx.clone().resolve_all();
if let Expr::Path(p) = &ctx.body.exprs[expr] {
let resolver = &ctx.resolver;
if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn) {
if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) {
return c;
}
}

View File

@ -17,7 +17,7 @@
use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
Scalar, TypeFlags,
Scalar, TyKind, TypeFlags,
};
use either::Either;
use hir_def::{
@ -44,7 +44,7 @@
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt,
};
// This lint has a false positive here. See the link below for details.
@ -118,7 +118,12 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
/// This is appropriate to use only after type-check: it assumes
/// that normalization will succeed, for example.
pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>, ty: Ty) -> Ty {
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
// FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only
// works for the type case, so we check array unconditionally. Remove the array part
// when the bug in chalk becomes fixed.
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION)
&& !matches!(ty.kind(Interner), TyKind::Array(..))
{
return ty;
}
let mut table = unify::InferenceTable::new(db, trait_env);

View File

@ -15,11 +15,11 @@
use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{
db::HirDatabase, fold_tys, fold_tys_and_consts, static_lifetime, to_chalk_trait_id,
traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex,
GenericArg, GenericArgData, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime,
ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty,
TyBuilder, TyExt, TyKind, VariableKind,
db::HirDatabase, fold_tys_and_consts, static_lifetime, to_chalk_trait_id, traits::FnTrait,
AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, GenericArg,
GenericArgData, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, ParamKind,
ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder,
TyExt, TyKind, VariableKind,
};
impl<'a> InferenceContext<'a> {
@ -236,13 +236,36 @@ pub(crate) fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{
fold_tys(
fold_tys_and_consts(
ty,
|ty, _| match ty.kind(Interner) {
TyKind::Alias(AliasTy::Projection(proj_ty)) => {
self.normalize_projection_ty(proj_ty.clone())
}
_ => ty,
|e, _| match e {
Either::Left(ty) => Either::Left(match ty.kind(Interner) {
TyKind::Alias(AliasTy::Projection(proj_ty)) => {
self.normalize_projection_ty(proj_ty.clone())
}
_ => ty,
}),
Either::Right(c) => Either::Right(match &c.data(Interner).value {
chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
crate::ConstScalar::UnevaluatedConst(c_id, subst) => {
// FIXME: Ideally here we should do everything that we do with type alias, i.e. adding a variable
// and registering an obligation. But it needs chalk support, so we handle the most basic
// case (a non associated const without generic parameters) manually.
if subst.len(Interner) == 0 {
if let Ok(eval) = self.db.const_eval((*c_id).into(), subst.clone())
{
eval
} else {
c
}
} else {
c
}
}
_ => c,
},
_ => c,
}),
},
DebruijnIndex::INNERMOST,
)

View File

@ -353,6 +353,14 @@ fn niche_optimization() {
}
}
#[test]
fn const_eval() {
size_and_align! {
const X: usize = 5;
struct Goal([i32; X]);
}
}
#[test]
fn enums_with_discriminants() {
size_and_align! {

View File

@ -2023,6 +2023,7 @@ pub(crate) fn const_or_path_to_chalk(
mode,
args,
debruijn,
expected_ty.clone(),
)
.unwrap_or_else(|| unknown_const(expected_ty))
}

View File

@ -3529,14 +3529,30 @@ fn main() {
#[test]
fn issue_14275() {
// FIXME: evaluate const generic
check_types(
r#"
struct Foo<const T: bool>;
fn main() {
const B: bool = false;
let foo = Foo::<B>;
//^^^ Foo<_>
//^^^ Foo<false>
}
"#,
);
check_types(
r#"
struct Foo<const T: bool>;
impl Foo<true> {
fn foo(self) -> u8 { 2 }
}
impl Foo<false> {
fn foo(self) -> u16 { 5 }
}
fn main() {
const B: bool = false;
let foo: Foo<B> = Foo;
let x = foo.foo();
//^ u16
}
"#,
);