From 14570df015d1641d1e382c9898e7c6d981b99e97 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 10 Apr 2020 17:44:43 +0200 Subject: Switch Chalk to recursive solver + various fixes related to that. --- crates/ra_hir_ty/src/autoderef.rs | 21 ++++++++++++++- crates/ra_hir_ty/src/infer/unify.rs | 1 + crates/ra_hir_ty/src/tests/traits.rs | 50 +++++++++++++++--------------------- crates/ra_hir_ty/src/traits.rs | 13 +++++----- crates/ra_hir_ty/src/traits/chalk.rs | 8 +++--- 5 files changed, 53 insertions(+), 40 deletions(-) diff --git a/crates/ra_hir_ty/src/autoderef.rs b/crates/ra_hir_ty/src/autoderef.rs index d91c21e24..1b0f84c5c 100644 --- a/crates/ra_hir_ty/src/autoderef.rs +++ b/crates/ra_hir_ty/src/autoderef.rs @@ -14,7 +14,7 @@ use crate::{ db::HirDatabase, traits::{InEnvironment, Solution}, utils::generics, - BoundVar, Canonical, DebruijnIndex, Substs, Ty, + BoundVar, Canonical, DebruijnIndex, Obligation, Substs, TraitRef, Ty, }; const AUTODEREF_RECURSION_LIMIT: usize = 10; @@ -66,6 +66,20 @@ fn deref_by_trait( let parameters = Substs::build_for_generics(&generic_params).push(ty.value.value.clone()).build(); + // Check that the type implements Deref at all + let trait_ref = TraitRef { trait_: deref_trait, substs: parameters.clone() }; + let implements_goal = super::Canonical { + num_vars: ty.value.num_vars, + value: InEnvironment { + value: Obligation::Trait(trait_ref), + environment: ty.environment.clone(), + }, + }; + if db.trait_solve(krate, implements_goal).is_none() { + return None; + } + + // Now do the assoc type projection let projection = super::traits::ProjectionPredicate { ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.num_vars)), projection_ty: super::ProjectionTy { associated_ty: target, parameters }, @@ -91,6 +105,11 @@ fn deref_by_trait( // they're just being 'passed through'. In the 'standard' case where // we have `impl Deref for Foo { Target = T }`, that should be // the case. + + // FIXME: if the trait solver decides to truncate the type, these + // assumptions will be broken. We would need to properly introduce + // new variables in that case + for i in 1..vars.0.num_vars { if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) { diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index ac25f8a80..5f6cea8d3 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -32,6 +32,7 @@ where var_stack: Vec, } +#[derive(Debug)] pub(super) struct Canonicalized { pub value: Canonical, free_vars: Vec, diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index b3a2fc439..0e4fd7bfd 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -349,7 +349,6 @@ trait Trait: SuperTrait { #[test] fn infer_project_associated_type() { - // y, z, a don't yet work because of https://github.com/rust-lang/chalk/issues/234 assert_snapshot!( infer(r#" trait Iterable { @@ -368,12 +367,12 @@ fn test() { [108; 261) '{ ...ter; }': () [118; 119) 'x': u32 [145; 146) '1': u32 - [156; 157) 'y': {unknown} - [183; 192) 'no_matter': {unknown} - [202; 203) 'z': {unknown} - [215; 224) 'no_matter': {unknown} - [234; 235) 'a': {unknown} - [249; 258) 'no_matter': {unknown} + [156; 157) 'y': Iterable::Item + [183; 192) 'no_matter': Iterable::Item + [202; 203) 'z': Iterable::Item + [215; 224) 'no_matter': Iterable::Item + [234; 235) 'a': Iterable::Item + [249; 258) 'no_matter': Iterable::Item "### ); } @@ -433,8 +432,8 @@ fn test>() { "#), @r###" [67; 100) '{ ...own; }': () - [77; 78) 'y': {unknown} - [90; 97) 'unknown': {unknown} + [77; 78) 'y': u32 + [90; 97) 'unknown': u32 "### ); } @@ -549,7 +548,7 @@ impl std::ops::Index for Bar { fn test() { let a = Bar; - let b = a[1]; + let b = a[1u32]; b<|>; } @@ -574,7 +573,7 @@ fn infer_ops_index_autoderef() { //- /main.rs crate:main deps:std fn test() { let a = &[1u32, 2, 3]; - let b = a[1]; + let b = a[1u32]; b<|>; } @@ -916,11 +915,7 @@ fn test(t: T) { } "#, ); - // FIXME here Chalk doesn't normalize the type to a placeholder. I think we - // need to add a rule like Normalize(::Out -> ApplyL::Out) - // to the trait env ourselves here; probably Chalk can't do this by itself. - // assert_eq!(t, "ApplyL::Out<[missing name]>"); - assert_eq!(t, "{unknown}"); + assert_eq!(t, "ApplyL::Out"); } #[test] @@ -1329,16 +1324,16 @@ fn test>(x: T, y: impl Trait) { [263; 264) 'y': impl Trait [290; 398) '{ ...r>); }': () [296; 299) 'get': fn get(T) -> ::Type - [296; 302) 'get(x)': {unknown} + [296; 302) 'get(x)': u32 [300; 301) 'x': T - [308; 312) 'get2': fn get2<{unknown}, T>(T) -> {unknown} - [308; 315) 'get2(x)': {unknown} + [308; 312) 'get2': fn get2(T) -> u32 + [308; 315) 'get2(x)': u32 [313; 314) 'x': T [321; 324) 'get': fn get>(impl Trait) -> as Trait>::Type - [321; 327) 'get(y)': {unknown} + [321; 327) 'get(y)': i64 [325; 326) 'y': impl Trait - [333; 337) 'get2': fn get2<{unknown}, impl Trait>(impl Trait) -> {unknown} - [333; 340) 'get2(y)': {unknown} + [333; 337) 'get2': fn get2>(impl Trait) -> i64 + [333; 340) 'get2(y)': i64 [338; 339) 'y': impl Trait [346; 349) 'get': fn get>(S) -> as Trait>::Type [346; 357) 'get(set(S))': u64 @@ -1402,7 +1397,6 @@ mod iter { #[test] fn projection_eq_within_chalk() { - // std::env::set_var("CHALK_DEBUG", "1"); assert_snapshot!( infer(r#" trait Trait1 { @@ -1422,7 +1416,7 @@ fn test>(x: T) { [164; 165) 'x': T [170; 186) '{ ...o(); }': () [176; 177) 'x': T - [176; 183) 'x.foo()': {unknown} + [176; 183) 'x.foo()': u32 "### ); } @@ -1578,7 +1572,7 @@ fn test u128>(f: F) { [150; 151) 'f': F [156; 184) '{ ...2)); }': () [162; 163) 'f': F - [162; 181) 'f.call...1, 2))': {unknown} + [162; 181) 'f.call...1, 2))': u128 [174; 180) '(1, 2)': (u32, u64) [175; 176) '1': u32 [178; 179) '2': u64 @@ -1829,7 +1823,7 @@ impl Trait for S2 { "#, ), @r###" [54; 58) 'self': &Self - [60; 61) 'x': {unknown} + [60; 61) 'x': Trait::Item [140; 144) 'self': &S [146; 147) 'x': u32 [161; 175) '{ let y = x; }': () @@ -1989,9 +1983,7 @@ fn test>>() { } "#, ); - // assert_eq!(t, "u32"); - // doesn't currently work, Chalk #234 - assert_eq!(t, "{unknown}"); + assert_eq!(t, "u32"); } #[test] diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 44fbdb197..05791a848 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -16,10 +16,12 @@ use self::chalk::{from_chalk, Interner, ToChalk}; pub(crate) mod chalk; mod builtin; -/// This controls the maximum size of types Chalk considers. If we set this too -/// high, we can run into slow edge cases; if we set it too low, Chalk won't -/// find some solutions. -const CHALK_SOLVER_MAX_SIZE: usize = 10; +// This controls the maximum size of types Chalk considers. If we set this too +// high, we can run into slow edge cases; if we set it too low, Chalk won't +// find some solutions. +// FIXME this is currently hardcoded in the recursive solver +// const CHALK_SOLVER_MAX_SIZE: usize = 10; + /// This controls how much 'time' we give the Chalk solver before giving up. const CHALK_SOLVER_FUEL: i32 = 100; @@ -30,8 +32,7 @@ struct ChalkContext<'a> { } fn create_chalk_solver() -> chalk_solve::Solver { - let solver_choice = - chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE, expected_answers: None }; + let solver_choice = chalk_solve::SolverChoice::recursive(); solver_choice.into_solver() } diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index b43e2a539..60d70d18e 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -511,13 +511,13 @@ impl ToChalk for ProjectionTy { } impl ToChalk for super::ProjectionPredicate { - type Chalk = chalk_ir::Normalize; + type Chalk = chalk_ir::AliasEq; - fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Normalize { - chalk_ir::Normalize { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) } + fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq { + chalk_ir::AliasEq { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) } } - fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::Normalize) -> Self { + fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq) -> Self { unimplemented!() } } -- cgit v1.2.3 From 39fe3a6486a2cbdf00bce8bd4861a900e0ff5811 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 13 Apr 2020 11:55:34 +0200 Subject: Test for non-working proc macro server assoc types --- crates/ra_hir_ty/src/tests/traits.rs | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 0e4fd7bfd..0a889f805 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1986,6 +1986,74 @@ fn test>>() { assert_eq!(t, "u32"); } +#[test] +fn proc_macro_server_types() { + assert_snapshot!( + infer_with_mismatches(r#" +macro_rules! with_api { + ($S:ident, $self:ident, $m:ident) => { + $m! { + TokenStream { + fn new() -> $S::TokenStream; + }, + Group { + }, + } + }; +} +macro_rules! associated_item { + (type TokenStream) => + (type TokenStream: 'static + Clone;); + (type Group) => + (type Group: 'static + Clone;); + ($($item:tt)*) => ($($item)*;) +} +macro_rules! declare_server_traits { + ($($name:ident { + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* + }),* $(,)?) => { + pub trait Types { + $(associated_item!(type $name);)* + } + + $(pub trait $name: Types { + $(associated_item!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* + })* + + pub trait Server: Types $(+ $name)* {} + impl Server for S {} + } +} +with_api!(Self, self_, declare_server_traits); +struct Group {} +struct TokenStream {} +struct Rustc; +impl Types for Rustc { + type TokenStream = TokenStream; + type Group = Group; +} +fn make() -> T { loop {} } +impl TokenStream for Rustc { + fn new() -> Self::TokenStream { + let group: Self::Group = make(); + make() + } +} +"#, true), + @r###" + [1115; 1126) '{ loop {} }': T + [1117; 1124) 'loop {}': ! + [1122; 1124) '{}': () + [1190; 1253) '{ ... }': {unknown} + [1204; 1209) 'group': {unknown} + [1225; 1229) 'make': fn make<{unknown}>() -> {unknown} + [1225; 1231) 'make()': {unknown} + [1241; 1245) 'make': fn make<{unknown}>() -> {unknown} + [1241; 1247) 'make()': {unknown} + "### + ); +} + #[test] fn unify_impl_trait() { assert_snapshot!( -- cgit v1.2.3