Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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.

2 changes: 1 addition & 1 deletion crates/hir-def/src/expr_store/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub struct AssociatedTypeBinding {
}

/// A single generic argument.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GenericArg {
Type(TypeRefId),
Lifetime(LifetimeRefId),
Expand Down
56 changes: 41 additions & 15 deletions crates/hir-ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@

use std::fmt;

use hir_def::TraitId;
use hir_def::{TypeAliasId, lang_item::LangItem};
use rustc_type_ir::inherent::{IntoKind, Ty as _};
use tracing::debug;
use triomphe::Arc;

use crate::next_solver::TraitRef;
use crate::next_solver::infer::InferOk;
use crate::next_solver::infer::traits::Obligation;
use crate::{
TraitEnvironment,
db::HirDatabase,
Expand Down Expand Up @@ -38,14 +41,14 @@ pub fn autoderef<'db>(
ty: crate::Canonical<crate::Ty>,
) -> impl Iterator<Item = crate::Ty> + use<> {
let mut table = InferenceTable::new(db, env);
let interner = table.interner;
let ty = table.instantiate_canonical(ty);
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty.to_nextsolver(interner));
let interner = table.interner();
let ty = table.instantiate_canonical(ty.to_nextsolver(interner));
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty);
let mut v = Vec::new();
while let Some((ty, _steps)) = autoderef.next() {
// `ty` may contain unresolved inference variables. Since there's no chance they would be
// resolved, just replace with fallback type.
let resolved = autoderef.table.resolve_completely(ty.to_chalk(interner));
let resolved = autoderef.table.resolve_completely(ty).to_chalk(interner);

// If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
// would revisit some already visited types. Stop here to avoid duplication.
Expand Down Expand Up @@ -101,6 +104,7 @@ struct AutoderefSnapshot<'db, Steps> {

#[derive(Clone, Copy)]
struct AutoderefTraits {
trait_: TraitId,
trait_target: TypeAliasId,
}

Expand Down Expand Up @@ -215,16 +219,26 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
Some(it) => Some(*it),
None => {
let traits = if self.use_receiver_trait {
AutoderefTraits {
trait_target: LangItem::ReceiverTarget
.resolve_type_alias(self.table.db, self.table.trait_env.krate)
.or_else(|| {
LangItem::DerefTarget
.resolve_type_alias(self.table.db, self.table.trait_env.krate)
})?,
}
(|| {
Some(AutoderefTraits {
trait_: LangItem::Receiver
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
trait_target: LangItem::ReceiverTarget
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
})
})()
.or_else(|| {
Some(AutoderefTraits {
trait_: LangItem::Deref
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
trait_target: LangItem::DerefTarget
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
})
})?
} else {
AutoderefTraits {
trait_: LangItem::Deref
.resolve_trait(self.table.db, self.table.trait_env.krate)?,
trait_target: LangItem::DerefTarget
.resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
}
Expand All @@ -236,10 +250,22 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {

fn overloaded_deref_ty(&mut self, ty: Ty<'db>) -> Option<Ty<'db>> {
debug!("overloaded_deref_ty({:?})", ty);
let interner = self.table.interner;
let interner = self.table.interner();

// <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk.
let AutoderefTraits { trait_target } = self.autoderef_traits()?;
let AutoderefTraits { trait_, trait_target } = self.autoderef_traits()?;

let trait_ref = TraitRef::new(interner, trait_.into(), [ty]);
let obligation =
Obligation::new(interner, ObligationCause::new(), self.table.trait_env.env, trait_ref);
// We detect whether the self type implements `Deref` before trying to
// structurally normalize. We use `predicate_may_hold_opaque_types_jank`
// to support not-yet-defined opaque types. It will succeed for `impl Deref`
// but fail for `impl OtherTrait`.
if !self.table.infer_ctxt.predicate_may_hold_opaque_types_jank(&obligation) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A bit of unrelated change, put it because I was clarified (#t-types > Do I need to prove T: Trait for <T as Trait>::Assoc = Foo?) that we do need to prove the obligation T: Deref first, before normalizing.

debug!("overloaded_deref_ty: cannot match obligation");
return None;
}

let (normalized_ty, obligations) = structurally_normalize_ty(
self.table,
Expand Down Expand Up @@ -316,7 +342,7 @@ pub(crate) fn overloaded_deref_ty<'db>(
table: &InferenceTable<'db>,
ty: Ty<'db>,
) -> Option<InferOk<'db, Ty<'db>>> {
let interner = table.interner;
let interner = table.interner();

let trait_target = LangItem::DerefTarget.resolve_type_alias(table.db, table.trait_env.krate)?;

Expand Down
16 changes: 11 additions & 5 deletions crates/hir-ty/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ use crate::{
error_lifetime,
generics::generics,
infer::unify::InferenceTable,
next_solver::{DbInterner, EarlyBinder, mapping::ChalkToNextSolver},
next_solver::{
DbInterner, EarlyBinder,
mapping::{ChalkToNextSolver, NextSolverToChalk},
},
primitive, to_assoc_type_id, to_chalk_trait_id,
};

Expand Down Expand Up @@ -141,10 +144,13 @@ impl<D> TyBuilder<D> {

#[tracing::instrument(skip_all)]
pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self {
self.fill(|x| match x {
ParamKind::Type => table.new_type_var().cast(Interner),
ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
ParamKind::Lifetime => table.new_lifetime_var().cast(Interner),
self.fill(|x| {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a bunch of code that I migrated from TyBuilder to using methods on GenericArgs and the like; I regret this a bit, as TyBuilder is more principled and also checks against misuse, but it's too late now. We will want to consider what to do with the rest of the code that uses it though.

match x {
ParamKind::Type => crate::next_solver::GenericArg::Ty(table.next_ty_var()),
ParamKind::Const(_) => table.next_const_var().into(),
ParamKind::Lifetime => table.next_region_var().into(),
}
.to_chalk(table.interner())
})
}

Expand Down
15 changes: 1 addition & 14 deletions crates/hir-ty/src/chalk_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! about the code that Chalk needs.
use hir_def::{CallableDefId, GenericDefId};

use crate::{Interner, Substitution, db::HirDatabase, mapping::from_chalk};
use crate::{Interner, db::HirDatabase, mapping::from_chalk};

pub(crate) type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
pub(crate) type TraitId = chalk_ir::TraitId<Interner>;
Expand Down Expand Up @@ -53,16 +53,3 @@ pub(crate) fn adt_variance_query(db: &dyn HirDatabase, adt_id: hir_def::AdtId) -
}),
)
}

/// Returns instantiated predicates.
pub(super) fn convert_where_clauses(
db: &dyn HirDatabase,
def: GenericDefId,
substs: &Substitution,
) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
db.generic_predicates(def)
.iter()
.cloned()
.map(|pred| pred.substitute(Interner, substs))
.collect()
}
102 changes: 31 additions & 71 deletions crates/hir-ty/src/consteval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use base_db::Crate;
use chalk_ir::{BoundVar, DebruijnIndex, cast::Cast};
use hir_def::{
EnumVariantId, GeneralConstId, HasModule as _, StaticId,
expr_store::{Body, HygieneId, path::Path},
hir::{Expr, ExprId},
expr_store::{HygieneId, path::Path},
hir::Expr,
resolver::{Resolver, ValueNs},
type_ref::LiteralConstRef,
};
Expand All @@ -19,13 +19,12 @@ use crate::{
db::HirDatabase,
display::DisplayTarget,
generics::Generics,
infer::InferenceContext,
lower::ParamLoweringMode,
next_solver::{DbInterner, mapping::ChalkToNextSolver},
to_placeholder_idx,
};

use super::mir::{MirEvalError, MirLowerError, interpret_mir, lower_to_mir, pad16};
use super::mir::{MirEvalError, MirLowerError, interpret_mir, pad16};

/// Extension trait for [`Const`]
pub trait ConstExt {
Expand Down Expand Up @@ -56,12 +55,12 @@ impl ConstExt for Const {
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstEvalError {
MirLowerError(MirLowerError),
MirEvalError(MirEvalError),
pub enum ConstEvalError<'db> {
MirLowerError(MirLowerError<'db>),
MirEvalError(MirEvalError<'db>),
}

impl ConstEvalError {
impl ConstEvalError<'_> {
pub fn pretty_print(
&self,
f: &mut String,
Expand All @@ -80,17 +79,17 @@ impl ConstEvalError {
}
}

impl From<MirLowerError> for ConstEvalError {
fn from(value: MirLowerError) -> Self {
impl<'db> From<MirLowerError<'db>> for ConstEvalError<'db> {
fn from(value: MirLowerError<'db>) -> Self {
match value {
MirLowerError::ConstEvalError(_, e) => *e,
_ => ConstEvalError::MirLowerError(value),
}
}
}

impl From<MirEvalError> for ConstEvalError {
fn from(value: MirEvalError) -> Self {
impl<'db> From<MirEvalError<'db>> for ConstEvalError<'db> {
fn from(value: MirEvalError<'db>) -> Self {
ConstEvalError::MirEvalError(value)
}
}
Expand Down Expand Up @@ -225,35 +224,35 @@ pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option<i128> {
}
}

pub(crate) fn const_eval_cycle_result(
_: &dyn HirDatabase,
pub(crate) fn const_eval_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: GeneralConstId,
_: Substitution,
_: Option<Arc<TraitEnvironment<'_>>>,
) -> Result<Const, ConstEvalError> {
_: Option<Arc<TraitEnvironment<'db>>>,
) -> Result<Const, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}

pub(crate) fn const_eval_static_cycle_result(
_: &dyn HirDatabase,
pub(crate) fn const_eval_static_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: StaticId,
) -> Result<Const, ConstEvalError> {
) -> Result<Const, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}

pub(crate) fn const_eval_discriminant_cycle_result(
_: &dyn HirDatabase,
pub(crate) fn const_eval_discriminant_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: EnumVariantId,
) -> Result<i128, ConstEvalError> {
) -> Result<i128, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}

pub(crate) fn const_eval_query(
db: &dyn HirDatabase,
pub(crate) fn const_eval_query<'db>(
db: &'db dyn HirDatabase,
def: GeneralConstId,
subst: Substitution,
trait_env: Option<Arc<TraitEnvironment<'_>>>,
) -> Result<Const, ConstEvalError> {
trait_env: Option<Arc<TraitEnvironment<'db>>>,
) -> Result<Const, ConstEvalError<'db>> {
let body = match def {
GeneralConstId::ConstId(c) => {
db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
Expand All @@ -267,10 +266,10 @@ pub(crate) fn const_eval_query(
Ok(c)
}

pub(crate) fn const_eval_static_query(
db: &dyn HirDatabase,
pub(crate) fn const_eval_static_query<'db>(
db: &'db dyn HirDatabase,
def: StaticId,
) -> Result<Const, ConstEvalError> {
) -> Result<Const, ConstEvalError<'db>> {
let body = db.monomorphized_mir_body(
def.into(),
Substitution::empty(Interner),
Expand All @@ -280,10 +279,10 @@ pub(crate) fn const_eval_static_query(
Ok(c)
}

pub(crate) fn const_eval_discriminant_variant(
db: &dyn HirDatabase,
pub(crate) fn const_eval_discriminant_variant<'db>(
db: &'db dyn HirDatabase,
variant_id: EnumVariantId,
) -> Result<i128, ConstEvalError> {
) -> Result<i128, ConstEvalError<'db>> {
let def = variant_id.into();
let body = db.body(def);
let loc = variant_id.lookup(db);
Expand Down Expand Up @@ -317,44 +316,5 @@ pub(crate) fn const_eval_discriminant_variant(
Ok(c)
}

// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should
// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here
// and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
pub(crate) fn eval_to_const(
expr: ExprId,
mode: ParamLoweringMode,
ctx: &mut InferenceContext<'_>,
debruijn: DebruijnIndex,
) -> Const {
let db = ctx.db;
let infer = ctx.fixme_resolve_all_clone();
fn has_closure(body: &Body, expr: ExprId) -> bool {
if matches!(body[expr], Expr::Closure { .. }) {
return true;
}
let mut r = false;
body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx));
r
}
if has_closure(ctx.body, expr) {
// Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
return unknown_const(infer[expr].clone());
}
if let Expr::Path(p) = &ctx.body[expr] {
let resolver = &ctx.resolver;
if let Some(c) =
path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone())
{
return c;
}
}
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr)
&& let Ok((Ok(result), _)) = interpret_mir(db, Arc::new(mir_body), true, None)
{
return result;
}
unknown_const(infer[expr].clone())
}

#[cfg(test)]
mod tests;
Loading