From 69e65118205dbde3601f01b9565537ed8004411e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 9 Jul 2021 19:12:56 +0200 Subject: [PATCH] Record autoderef adjustments --- crates/hir_ty/src/autoderef.rs | 75 +++++++++++++++++++- crates/hir_ty/src/infer/coerce.rs | 65 +++++++++++++---- crates/hir_ty/src/infer/expr.rs | 50 ++++++++----- crates/hir_ty/src/lib.rs | 1 + crates/hir_ty/src/tests/coercion.rs | 1 + crates/hir_ty/src/tests/method_resolution.rs | 55 +++----------- 6 files changed, 167 insertions(+), 80 deletions(-) diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index 2c07494a97..fbbb0c8208 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs @@ -13,12 +13,83 @@ use log::{info, warn}; use crate::{ db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, - DebruijnIndex, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, Ty, TyBuilder, - TyKind, + DebruijnIndex, Environment, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, + Ty, TyBuilder, TyKind, }; +pub(crate) enum AutoderefKind { + Builtin, + Overloaded, +} + +pub(crate) struct Autoderef<'db> { + db: &'db dyn HirDatabase, + ty: Canonical, + at_start: bool, + krate: Option, + environment: Environment, + steps: Vec<(AutoderefKind, Ty)>, +} + +impl<'db> Autoderef<'db> { + pub(crate) fn new( + db: &'db dyn HirDatabase, + krate: Option, + ty: InEnvironment>, + ) -> Self { + let InEnvironment { goal: ty, environment } = ty; + Autoderef { db, ty, at_start: true, environment, krate, steps: Vec::new() } + } + + pub(crate) fn step_count(&self) -> usize { + self.steps.len() + } + + pub(crate) fn steps(&self) -> &[(AutoderefKind, chalk_ir::Ty)] { + &self.steps + } + + pub(crate) fn final_ty(&self) -> Ty { + self.ty.value.clone() + } +} + +impl Iterator for Autoderef<'_> { + type Item = (Canonical, usize); + + fn next(&mut self) -> Option { + if self.at_start { + self.at_start = false; + return Some((self.ty.clone(), 0)); + } + + if self.steps.len() >= AUTODEREF_RECURSION_LIMIT { + return None; + } + + let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) { + (AutoderefKind::Builtin, Canonical { value: derefed, binders: self.ty.binders.clone() }) + } else { + ( + AutoderefKind::Overloaded, + deref_by_trait( + self.db, + self.krate?, + InEnvironment { goal: &self.ty, environment: self.environment.clone() }, + )?, + ) + }; + + self.steps.push((kind, self.ty.value.clone())); + self.ty = new_ty; + + Some((self.ty.clone(), self.step_count())) + } +} + const AUTODEREF_RECURSION_LIMIT: usize = 10; +// FIXME: replace uses of this with Autoderef above pub fn autoderef<'a>( db: &'a dyn HirDatabase, krate: Option, diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 2599a8c6b1..d2e999a730 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -5,14 +5,17 @@ //! See and //! `librustc_typeck/check/coercion.rs`. +use std::iter; + use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind}; use hir_def::{expr::ExprId, lang_item::LangItemTarget}; +use stdx::always; use crate::{ - autoderef, + autoderef::{Autoderef, AutoderefKind}, infer::{ - Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, PointerCast, - TypeError, TypeMismatch, + Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, OverloadedDeref, + PointerCast, TypeError, TypeMismatch, }, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, InEnvironment, Interner, Solution, Substitution, Ty, TyBuilder, TyExt, TyKind, @@ -241,9 +244,10 @@ impl<'a> InferenceContext<'a> { /// 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 { - match from_ty.kind(&Interner) { - TyKind::Ref(mt, _, _) => { - coerce_mutabilities(*mt, to_mt)?; + let from_mt = match from_ty.kind(&Interner) { + &TyKind::Ref(mt, _, _) => { + coerce_mutabilities(mt, to_mt)?; + mt } _ => return self.unify_and(&from_ty, to_ty, identity), }; @@ -254,8 +258,8 @@ impl<'a> InferenceContext<'a> { // details of coercion errors though, so I think it's useful to leave // the structure like it is. - let canonicalized = self.canonicalize(from_ty); - let autoderef = autoderef::autoderef( + let canonicalized = self.canonicalize(from_ty.clone()); + let mut autoderef = Autoderef::new( self.db, self.resolver.krate(), InEnvironment { @@ -266,7 +270,7 @@ impl<'a> InferenceContext<'a> { let mut first_error = None; let mut found = None; - for (autoderefs, referent_ty) in autoderef.enumerate() { + for (referent_ty, autoderefs) in autoderef.by_ref() { if autoderefs == 0 { // Don't let this pass, otherwise it would cause // &T to autoref to &&T. @@ -324,12 +328,43 @@ impl<'a> InferenceContext<'a> { return Err(err); } }; - // FIXME: record overloaded deref adjustments - success( - vec![Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() }], - ty, - goals, - ) + if ty == from_ty && from_mt == Mutability::Not && autoderef.step_count() == 1 { + // As a special case, if we would produce `&'a *x`, that's + // a total no-op. We end up with the type `&'a T` just as + // we started with. In that case, just skip it + // altogether. This is just an optimization. + // + // Note that for `&mut`, we DO want to reborrow -- + // otherwise, this would be a move, which might be an + // error. For example `foo(self.x)` where `self` and + // `self.x` both have `&mut `type would be a move of + // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`, + // which is a borrow. + always!(to_mt == Mutability::Not); // can only coerce &T -> &U + return success(vec![], ty, goals); + } + + let mut adjustments = self.auto_deref_adjust_steps(&autoderef); + adjustments + .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() }); + + success(adjustments, ty, goals) + } + + pub(super) fn auto_deref_adjust_steps(&self, autoderef: &Autoderef<'_>) -> Vec { + let steps = autoderef.steps(); + let targets = + steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty())); + steps + .iter() + .map(|(kind, _source)| match kind { + // We do not know what kind of deref we require at this point yet + AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)), + AutoderefKind::Builtin => None, + }) + .zip(targets) + .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) + .collect() } /// Attempts to coerce from the type of a Rust function item into a function pointer. diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 9904676096..f1bc6895c8 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -1,7 +1,10 @@ //! Type inference for expressions. -use std::iter::{repeat, repeat_with}; -use std::{mem, sync::Arc}; +use std::{ + iter::{repeat, repeat_with}, + mem, + sync::Arc, +}; use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind}; use hir_def::{ @@ -15,7 +18,8 @@ use stdx::always; use syntax::ast::RangeOp; use crate::{ - autoderef, consteval, + autoderef::{self, Autoderef}, + consteval, infer::coerce::CoerceMany, lower::lower_to_chalk_mutability, mapping::from_chalk, @@ -314,7 +318,7 @@ impl<'a> InferenceContext<'a> { Expr::Call { callee, args } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); let canonicalized = self.canonicalize(callee_ty.clone()); - let mut derefs = autoderef( + let mut derefs = Autoderef::new( self.db, self.resolver.krate(), InEnvironment { @@ -322,14 +326,19 @@ impl<'a> InferenceContext<'a> { environment: self.table.trait_env.env.clone(), }, ); - let (param_tys, ret_ty): (Vec, Ty) = derefs - .find_map(|callee_deref_ty| { - self.callable_sig( - &canonicalized.decanonicalize_ty(callee_deref_ty.value), - args.len(), - ) - }) - .unwrap_or((Vec::new(), self.err_ty())); + let res = derefs.by_ref().find_map(|(callee_deref_ty, _)| { + self.callable_sig( + &canonicalized.decanonicalize_ty(callee_deref_ty.value), + args.len(), + ) + }); + let (param_tys, ret_ty): (Vec, Ty) = match res { + Some(res) => { + self.write_expr_adj(*callee, self.auto_deref_adjust_steps(&derefs)); + res + } + None => (Vec::new(), self.err_ty()), + }; self.register_obligations_for_call(&callee_ty); self.check_call_arguments(args, ¶m_tys); self.normalize_associated_types_in(ret_ty) @@ -467,15 +476,16 @@ impl<'a> InferenceContext<'a> { Expr::Field { expr, name } => { let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); let canonicalized = self.canonicalize(receiver_ty); - let ty = autoderef::autoderef( + + let mut autoderef = Autoderef::new( self.db, self.resolver.krate(), InEnvironment { goal: canonicalized.value.clone(), environment: self.trait_env.env.clone(), }, - ) - .find_map(|derefed_ty| { + ); + let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| { let def_db = self.db.upcast(); let module = self.resolver.module(); let is_visible = |field_id: &FieldId| { @@ -524,8 +534,14 @@ impl<'a> InferenceContext<'a> { } _ => None, } - }) - .unwrap_or_else(|| self.err_ty()); + }); + let ty = match ty { + Some(ty) => { + self.write_expr_adj(*expr, self.auto_deref_adjust_steps(&autoderef)); + ty + } + None => self.err_ty(), + }; let ty = self.insert_type_vars(ty); self.normalize_associated_types_in(ty) } diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 128cae830f..2026a42682 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -112,6 +112,7 @@ pub type Canonical = chalk_ir::Canonical; pub type FnSig = chalk_ir::FnSig; pub type InEnvironment = chalk_ir::InEnvironment; +pub type Environment = chalk_ir::Environment; pub type DomainGoal = chalk_ir::DomainGoal; pub type Goal = chalk_ir::Goal; pub type AliasEq = chalk_ir::AliasEq; diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index cc5dc5ab65..1e30e2e8c1 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -236,6 +236,7 @@ fn takes_ref_str(x: &str) {} fn returns_string() -> String { loop {} } fn test() { takes_ref_str(&{ returns_string() }); + // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not)) } "#, ); diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 3f7a37295e..3bf67281f2 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -1,5 +1,7 @@ use expect_test::expect; +use crate::tests::check; + use super::{check_infer, check_types}; #[test] @@ -1137,7 +1139,7 @@ fn main() { #[test] fn autoderef_visibility_field() { - check_infer( + check( r#" //- minicore: deref mod a { @@ -1158,33 +1160,18 @@ mod a { mod b { fn foo() { let x = super::a::Bar::new().0; + // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Not))) + // ^^^^^^^^^^^^^^^^^^^^^^ type: char } } - "#, - expect![[r#" - 107..138 '{ ... }': Bar - 121..125 'Self': Bar(i32) -> Bar - 121..128 'Self(0)': Bar - 126..127 '0': i32 - 226..230 'self': &Bar - 240..273 '{ ... }': &Foo - 254..263 '&Foo('z')': &Foo - 255..258 'Foo': Foo(char) -> Foo - 255..263 'Foo('z')': Foo - 259..262 ''z'': char - 303..350 '{ ... }': () - 317..318 'x': char - 321..339 'super:...r::new': fn new() -> Bar - 321..341 'super:...:new()': Bar - 321..343 'super:...ew().0': char - "#]], +"#, ) } #[test] fn autoderef_visibility_method() { cov_mark::check!(autoderef_candidate_not_visible); - check_infer( + check( r#" //- minicore: deref mod a { @@ -1213,34 +1200,10 @@ mod a { mod b { fn foo() { let x = super::a::Bar::new().mango(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: char } } - "#, - expect![[r#" - 75..79 'self': &Foo - 89..119 '{ ... }': char - 103..107 'self': &Foo - 103..109 'self.0': char - 195..226 '{ ... }': Bar - 209..213 'Self': Bar(i32) -> Bar - 209..216 'Self(0)': Bar - 214..215 '0': i32 - 245..249 'self': &Bar - 258..288 '{ ... }': i32 - 272..276 'self': &Bar - 272..278 'self.0': i32 - 376..380 'self': &Bar - 390..423 '{ ... }': &Foo - 404..413 '&Foo('z')': &Foo - 405..408 'Foo': Foo(char) -> Foo - 405..413 'Foo('z')': Foo - 409..412 ''z'': char - 453..506 '{ ... }': () - 467..468 'x': char - 471..489 'super:...r::new': fn new() -> Bar - 471..491 'super:...:new()': Bar - 471..499 'super:...ango()': char - "#]], +"#, ) }