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
21 changes: 20 additions & 1 deletion crates/ra_hir_ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 },
Expand All @@ -91,6 +105,11 @@ fn deref_by_trait(
// they're just being 'passed through'. In the 'standard' case where
// we have `impl<T> Deref for Foo<T> { 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))
{
Expand Down
1 change: 1 addition & 0 deletions crates/ra_hir_ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ where
var_stack: Vec<TypeVarId>,
}

#[derive(Debug)]
pub(super) struct Canonicalized<T> {
pub value: Canonical<T>,
free_vars: Vec<InferTy>,
Expand Down
118 changes: 89 additions & 29 deletions crates/ra_hir_ty/src/tests/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -368,12 +367,12 @@ fn test<T: Iterable>() {
[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<T>
[183; 192) 'no_matter': Iterable::Item<T>
[202; 203) 'z': Iterable::Item<T>
[215; 224) 'no_matter': Iterable::Item<T>
[234; 235) 'a': Iterable::Item<T>
[249; 258) 'no_matter': Iterable::Item<T>
"###
);
}
Expand Down Expand Up @@ -433,8 +432,8 @@ fn test<T: Iterable<Item=u32>>() {
"#),
@r###"
[67; 100) '{ ...own; }': ()
[77; 78) 'y': {unknown}
[90; 97) 'unknown': {unknown}
[77; 78) 'y': u32
[90; 97) 'unknown': u32
"###
);
}
Expand Down Expand Up @@ -549,7 +548,7 @@ impl std::ops::Index<u32> for Bar {

fn test() {
let a = Bar;
let b = a[1];
let b = a[1u32];
b<|>;
}

Expand All @@ -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<|>;
}

Expand Down Expand Up @@ -916,11 +915,7 @@ fn test<T: ApplyL>(t: T) {
}
"#,
);
// FIXME here Chalk doesn't normalize the type to a placeholder. I think we
// need to add a rule like Normalize(<T as ApplyL>::Out -> ApplyL::Out<T>)
// 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<T>");
}

#[test]
Expand Down Expand Up @@ -1329,16 +1324,16 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
[263; 264) 'y': impl Trait<Type = i64>
[290; 398) '{ ...r>); }': ()
[296; 299) 'get': fn get<T>(T) -> <T as Trait>::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<u32, T>(T) -> u32
[308; 315) 'get2(x)': u32
[313; 314) 'x': T
[321; 324) 'get': fn get<impl Trait<Type = i64>>(impl Trait<Type = i64>) -> <impl Trait<Type = i64> as Trait>::Type
[321; 327) 'get(y)': {unknown}
[321; 327) 'get(y)': i64
[325; 326) 'y': impl Trait<Type = i64>
[333; 337) 'get2': fn get2<{unknown}, impl Trait<Type = i64>>(impl Trait<Type = i64>) -> {unknown}
[333; 340) 'get2(y)': {unknown}
[333; 337) 'get2': fn get2<i64, impl Trait<Type = i64>>(impl Trait<Type = i64>) -> i64
[333; 340) 'get2(y)': i64
[338; 339) 'y': impl Trait<Type = i64>
[346; 349) 'get': fn get<S<u64>>(S<u64>) -> <S<u64> as Trait>::Type
[346; 357) 'get(set(S))': u64
Expand Down Expand Up @@ -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 {
Expand All @@ -1422,7 +1416,7 @@ fn test<T: Trait1<Type = u32>>(x: T) {
[164; 165) 'x': T
[170; 186) '{ ...o(); }': ()
[176; 177) 'x': T
[176; 183) 'x.foo()': {unknown}
[176; 183) 'x.foo()': u32
"###
);
}
Expand Down Expand Up @@ -1578,7 +1572,7 @@ fn test<F: FnOnce(u32, u64) -> 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
Expand Down Expand Up @@ -1829,7 +1823,7 @@ impl Trait for S2 {
"#,
), @r###"
[54; 58) 'self': &Self
[60; 61) 'x': {unknown}
[60; 61) 'x': Trait::Item<Self>
[140; 144) 'self': &S
[146; 147) 'x': u32
[161; 175) '{ let y = x; }': ()
Expand Down Expand Up @@ -1989,9 +1983,75 @@ fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
}
"#,
);
// assert_eq!(t, "u32");
// doesn't currently work, Chalk #234
assert_eq!(t, "{unknown}");
assert_eq!(t, "u32");
}

#[test]
fn proc_macro_server_types() {
Copy link
Member Author

Choose a reason for hiding this comment

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

ah btw this is code from RA that isn't working yet; even though we seem to be able to expand the macro, if I write the traits without macros I can't reproduce the problem. So it might be some macro issue, I'm not sure. cc @edwin0cheng

Copy link
Contributor

@edwin0cheng edwin0cheng Apr 19, 2020

Choose a reason for hiding this comment

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

I just checked, it is due to the fact that we still not support mbe in trait items yet :) I opened #4039 to track the issue.

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<S: Types $(+ $name)*> 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>() -> 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]
Expand Down
13 changes: 7 additions & 6 deletions crates/ra_hir_ty/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -30,8 +32,7 @@ struct ChalkContext<'a> {
}

fn create_chalk_solver() -> chalk_solve::Solver<Interner> {
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()
}

Expand Down
8 changes: 4 additions & 4 deletions crates/ra_hir_ty/src/traits/chalk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,13 +511,13 @@ impl ToChalk for ProjectionTy {
}

impl ToChalk for super::ProjectionPredicate {
type Chalk = chalk_ir::Normalize<Interner>;
type Chalk = chalk_ir::AliasEq<Interner>;

fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Normalize<Interner> {
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<Interner> {
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<Interner>) -> Self {
fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self {
unimplemented!()
}
}
Expand Down