Skip to content

Commit

Permalink
implement basic inferring of lifetimes
Browse files Browse the repository at this point in the history
  • Loading branch information
dfireBird committed Jul 22, 2024
1 parent 329adb5 commit b9e9e29
Show file tree
Hide file tree
Showing 14 changed files with 110 additions and 86 deletions.
20 changes: 10 additions & 10 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ use triomphe::Arc;

use crate::{
db::HirDatabase,
error_lifetime, fold_tys,
fold_tys,
generics::Generics,
infer::{coerce::CoerceMany, unify::InferenceTable},
lower::ImplTraitLoweringMode,
Expand Down Expand Up @@ -329,13 +329,13 @@ pub struct Adjustment {
}

impl Adjustment {
pub fn borrow(m: Mutability, ty: Ty) -> Self {
let ty = TyKind::Ref(m, error_lifetime(), ty).intern(Interner);
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
pub fn borrow(m: Mutability, ty: Ty, lt: Lifetime) -> Self {
let ty = TyKind::Ref(m, lt.clone(), ty).intern(Interner);
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(lt, m)), target: ty }
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adjust {
/// Go from ! to any type.
NeverToAny,
Expand All @@ -355,18 +355,18 @@ pub enum Adjust {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct OverloadedDeref(pub Option<Mutability>);

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum AutoBorrow {
/// Converts from T to &T.
Ref(Mutability),
Ref(Lifetime, Mutability),
/// Converts from T to *T.
RawPtr(Mutability),
}

impl AutoBorrow {
fn mutability(self) -> Mutability {
let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self;
m
fn mutability(&self) -> Mutability {
let (AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) = self;
*m
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/hir-ty/src/infer/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ impl CapturedItemWithoutTy {
BorrowKind::Mut { .. } => Mutability::Mut,
_ => Mutability::Not,
};
// FIXME: use lifetime inference here
TyKind::Ref(m, error_lifetime(), ty).intern(Interner)
}
};
Expand Down Expand Up @@ -474,7 +475,7 @@ impl InferenceContext<'_> {

fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) {
if let Some((last, rest)) = adjustment.split_last() {
match last.kind {
match &last.kind {
Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => {
self.walk_expr_with_adjust(tgt_expr, rest)
}
Expand Down
37 changes: 22 additions & 15 deletions crates/hir-ty/src/infer/coerce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ use triomphe::Arc;
use crate::{
autoderef::{Autoderef, AutoderefKind},
db::HirDatabase,
error_lifetime,
infer::{
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
TypeError, TypeMismatch,
},
utils::ClosureSubst,
Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Lifetime,
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
};

use super::unify::InferenceTable;
Expand Down Expand Up @@ -301,7 +300,7 @@ impl InferenceTable<'_> {
// Examine the supertype and consider auto-borrowing.
match to_ty.kind(Interner) {
TyKind::Raw(mt, _) => return self.coerce_ptr(from_ty, to_ty, *mt),
TyKind::Ref(mt, _, _) => return self.coerce_ref(from_ty, to_ty, *mt),
TyKind::Ref(mt, lt, _) => return self.coerce_ref(from_ty, to_ty, *mt, lt),
_ => {}
}

Expand Down Expand Up @@ -377,11 +376,17 @@ impl InferenceTable<'_> {
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
/// To match `A` with `B`, autoderef will be performed,
/// calling `deref`/`deref_mut` where necessary.
fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult {
let from_mt = match from_ty.kind(Interner) {
&TyKind::Ref(mt, _, _) => {
coerce_mutabilities(mt, to_mt)?;
mt
fn coerce_ref(
&mut self,
from_ty: Ty,
to_ty: &Ty,
to_mt: Mutability,
to_lt: &Lifetime,
) -> CoerceResult {
let (_from_lt, from_mt) = match from_ty.kind(Interner) {
TyKind::Ref(mt, lt, _) => {
coerce_mutabilities(*mt, to_mt)?;
(lt.clone(), *mt) // clone is probably not good?
}
_ => return self.unify_and(&from_ty, to_ty, identity),
};
Expand Down Expand Up @@ -427,8 +432,8 @@ impl InferenceTable<'_> {
// compare those. Note that this means we use the target
// mutability [1], since it may be that we are coercing
// from `&mut T` to `&U`.
let lt = error_lifetime(); // FIXME: handle lifetimes correctly, see rustc
let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner);
let lt = to_lt; // FIXME: Involve rustc LUB and SUB flag checks
let derefd_from_ty = TyKind::Ref(to_mt, lt.clone(), referent_ty).intern(Interner);
match autoderef.table.try_unify(&derefd_from_ty, to_ty) {
Ok(result) => {
found = Some(result.map(|()| derefd_from_ty));
Expand Down Expand Up @@ -472,8 +477,10 @@ impl InferenceTable<'_> {
}

let mut adjustments = auto_deref_adjust_steps(&autoderef);
adjustments
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() });
adjustments.push(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(to_lt.clone(), to_mt)),
target: ty.clone(),
});

success(adjustments, ty, goals)
}
Expand Down Expand Up @@ -621,11 +628,11 @@ impl InferenceTable<'_> {
(TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => {
coerce_mutabilities(*from_mt, to_mt)?;

let lt = error_lifetime();
let lt = self.new_lifetime_var();
Some((
Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() },
Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)),
kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), to_mt)),
target: TyKind::Ref(to_mt, lt, from_inner.clone()).intern(Interner),
},
))
Expand Down
40 changes: 29 additions & 11 deletions crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use crate::{
autoderef::{builtin_deref, deref_by_trait, Autoderef},
consteval,
db::{InternedClosure, InternedCoroutine},
error_lifetime,
generics::{generics, Generics},
infer::{
coerce::{CoerceMany, CoercionCause},
Expand Down Expand Up @@ -635,7 +634,10 @@ impl InferenceContext<'_> {
let inner_ty = self.infer_expr_inner(*expr, &expectation);
match rawness {
Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
Rawness::Ref => TyKind::Ref(mutability, error_lifetime(), inner_ty),
Rawness::Ref => {
let lt = self.table.new_lifetime_var();
TyKind::Ref(mutability, lt, inner_ty)
}
}
.intern(Interner)
}
Expand Down Expand Up @@ -786,7 +788,11 @@ impl InferenceContext<'_> {
adj.apply(&mut self.table, base_ty)
});
// mutability will be fixed up in `InferenceContext::infer_mut`;
adj.push(Adjustment::borrow(Mutability::Not, self_ty.clone()));
adj.push(Adjustment::borrow(
Mutability::Not,
self_ty.clone(),
self.table.new_lifetime_var(),
));
self.write_expr_adj(*base, adj);
if let Some(func) = self
.db
Expand Down Expand Up @@ -990,7 +996,7 @@ impl InferenceContext<'_> {
match fn_x {
FnTrait::FnOnce => (),
FnTrait::FnMut => {
if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) {
if let TyKind::Ref(Mutability::Mut, lt, inner) = derefed_callee.kind(Interner) {
if adjustments
.last()
.map(|it| matches!(it.kind, Adjust::Borrow(_)))
Expand All @@ -999,15 +1005,27 @@ impl InferenceContext<'_> {
// prefer reborrow to move
adjustments
.push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() });
adjustments.push(Adjustment::borrow(Mutability::Mut, inner.clone()))
adjustments.push(Adjustment::borrow(
Mutability::Mut,
inner.clone(),
lt.clone(),
))
}
} else {
adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone()));
adjustments.push(Adjustment::borrow(
Mutability::Mut,
derefed_callee.clone(),
self.table.new_lifetime_var(),
));
}
}
FnTrait::Fn => {
if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) {
adjustments.push(Adjustment::borrow(Mutability::Not, derefed_callee.clone()));
adjustments.push(Adjustment::borrow(
Mutability::Not,
derefed_callee.clone(),
self.table.new_lifetime_var(),
));
}
}
}
Expand Down Expand Up @@ -1310,23 +1328,23 @@ impl InferenceContext<'_> {
Some(sig) => {
let p_left = &sig.params()[0];
if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) {
if let &TyKind::Ref(mtbl, _, _) = p_left.kind(Interner) {
if let TyKind::Ref(mtbl, lt, _) = p_left.kind(Interner) {
self.write_expr_adj(
lhs,
vec![Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mtbl)),
kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)),
target: p_left.clone(),
}],
);
}
}
let p_right = &sig.params()[1];
if matches!(op, BinaryOp::CmpOp(..)) {
if let &TyKind::Ref(mtbl, _, _) = p_right.kind(Interner) {
if let TyKind::Ref(mtbl, lt, _) = p_right.kind(Interner) {
self.write_expr_adj(
rhs,
vec![Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mtbl)),
kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)),
target: p_right.clone(),
}],
);
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/infer/mutability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl InferenceContext<'_> {
Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (),
Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)),
Adjust::Borrow(b) => match b {
AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m) => mutability = *m,
AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m) => mutability = *m,
},
}
}
Expand Down Expand Up @@ -121,7 +121,7 @@ impl InferenceContext<'_> {
.get_mut(&base)
.and_then(|it| it.last_mut());
if let Some(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)),
..
}) = base_adjustments
{
Expand Down
17 changes: 9 additions & 8 deletions crates/hir-ty/src/infer/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use stdx::TupleExt;

use crate::{
consteval::{try_const_usize, usize_const},
error_lifetime,
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
lower::lower_to_chalk_mutability,
primitive::UintTy,
Expand Down Expand Up @@ -394,19 +393,20 @@ impl InferenceContext<'_> {
expected: &Ty,
default_bm: BindingMode,
) -> Ty {
let expectation = match expected.as_reference() {
Some((inner_ty, _lifetime, _exp_mut)) => inner_ty.clone(),
let (expectation_type, expection_lt) = match expected.as_reference() {
Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()),
None => {
let inner_ty = self.table.new_type_var();
let inner_lt = self.table.new_lifetime_var();
let ref_ty =
TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner);
TyKind::Ref(mutability, inner_lt.clone(), inner_ty.clone()).intern(Interner);
// Unification failure will be reported by the caller.
self.unify(&ref_ty, expected);
inner_ty
(inner_ty, inner_lt)
}
};
let subty = self.infer_pat(inner_pat, &expectation, default_bm);
TyKind::Ref(mutability, error_lifetime(), subty).intern(Interner)
let subty = self.infer_pat(inner_pat, &expectation_type, default_bm);
TyKind::Ref(mutability, expection_lt, subty).intern(Interner)
}

fn infer_bind_pat(
Expand All @@ -433,7 +433,8 @@ impl InferenceContext<'_> {

let bound_ty = match mode {
BindingMode::Ref(mutability) => {
TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner)
let inner_lt = self.table.new_lifetime_var();
TyKind::Ref(mutability, inner_lt, inner_ty.clone()).intern(Interner)
}
BindingMode::Move => inner_ty.clone(),
};
Expand Down
14 changes: 7 additions & 7 deletions crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ use triomphe::Arc;

use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{
consteval::unknown_const, db::HirDatabase, error_lifetime, fold_generic_args,
fold_tys_and_consts, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical,
Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData,
Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy,
ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
TyKind, VariableKind, WhereClause,
consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts,
to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue,
DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment,
InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar,
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
WhereClause,
};

impl InferenceContext<'_> {
Expand Down Expand Up @@ -105,7 +105,7 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner),
// Chalk can sometimes return new lifetime variables. We just replace them by errors
// for now.
VariableKind::Lifetime => error_lifetime().cast(Interner),
VariableKind::Lifetime => ctx.new_lifetime_var().cast(Interner),
VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner),
}),
);
Expand Down
3 changes: 2 additions & 1 deletion crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,8 @@ impl ReceiverAdjustments {
}
}
if let Some(m) = self.autoref {
let a = Adjustment::borrow(m, ty);
let lt = table.new_lifetime_var();
let a = Adjustment::borrow(m, ty, lt);
ty = a.target.clone();
adjust.push(a);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/mir/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
Ok(Some(current))
}
Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => {
Adjust::Borrow(AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) => {
let Some((p, current)) =
self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)?
else {
Expand Down
Loading

0 comments on commit b9e9e29

Please sign in to comment.