mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
3964: Nicer Chalk debug logs r=matklad a=flodiebold
I'm looking at a lot of Chalk debug logs at the moment, so here's a few changes to make them slightly nicer...
3965: Implement inline associated type bounds r=matklad a=flodiebold
Like `Iterator<Item: SomeTrait>`.
This is an unstable feature, but it's used in the standard library e.g. in the definition of Flatten, so we can't get away with not implementing it :)
(This is cherry-picked from my recursive solver branch, where it works better, but I did manage to write a test that works with the current Chalk solver as well...)
3967: Handle `Self::Type` in trait definitions when referring to own associated type r=matklad a=flodiebold
It was implemented for other generic parameters for the trait, but not for `Self`.
(Last one off my recursive solver branch 😄 )
Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
d61909f904
10 changed files with 192 additions and 32 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1014,6 +1014,7 @@ dependencies = [
|
||||||
"chalk-solve",
|
"chalk-solve",
|
||||||
"ena",
|
"ena",
|
||||||
"insta",
|
"insta",
|
||||||
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"ra_arena",
|
"ra_arena",
|
||||||
"ra_db",
|
"ra_db",
|
||||||
|
|
|
@ -15,7 +15,7 @@ use ra_syntax::ast::{
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::Attrs,
|
attr::Attrs,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
path::{path, GenericArgs, Path},
|
path::{path, AssociatedTypeBinding, GenericArgs, Path},
|
||||||
src::HasSource,
|
src::HasSource,
|
||||||
type_ref::{Mutability, TypeBound, TypeRef},
|
type_ref::{Mutability, TypeBound, TypeRef},
|
||||||
visibility::RawVisibility,
|
visibility::RawVisibility,
|
||||||
|
@ -95,7 +95,11 @@ fn desugar_future_path(orig: TypeRef) -> Path {
|
||||||
let path = path![std::future::Future];
|
let path = path![std::future::Future];
|
||||||
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
|
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
|
||||||
let mut last = GenericArgs::empty();
|
let mut last = GenericArgs::empty();
|
||||||
last.bindings.push((name![Output], orig));
|
last.bindings.push(AssociatedTypeBinding {
|
||||||
|
name: name![Output],
|
||||||
|
type_ref: Some(orig),
|
||||||
|
bounds: Vec::new(),
|
||||||
|
});
|
||||||
generic_args.push(Some(Arc::new(last)));
|
generic_args.push(Some(Arc::new(last)));
|
||||||
|
|
||||||
Path::from_known_path(path, generic_args)
|
Path::from_known_path(path, generic_args)
|
||||||
|
|
|
@ -14,7 +14,10 @@ use hir_expand::{
|
||||||
use ra_db::CrateId;
|
use ra_db::CrateId;
|
||||||
use ra_syntax::ast;
|
use ra_syntax::ast;
|
||||||
|
|
||||||
use crate::{type_ref::TypeRef, InFile};
|
use crate::{
|
||||||
|
type_ref::{TypeBound, TypeRef},
|
||||||
|
InFile,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ModPath {
|
pub struct ModPath {
|
||||||
|
@ -111,7 +114,21 @@ pub struct GenericArgs {
|
||||||
/// is left out.
|
/// is left out.
|
||||||
pub has_self_type: bool,
|
pub has_self_type: bool,
|
||||||
/// Associated type bindings like in `Iterator<Item = T>`.
|
/// Associated type bindings like in `Iterator<Item = T>`.
|
||||||
pub bindings: Vec<(Name, TypeRef)>,
|
pub bindings: Vec<AssociatedTypeBinding>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An associated type binding like in `Iterator<Item = T>`.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct AssociatedTypeBinding {
|
||||||
|
/// The name of the associated type.
|
||||||
|
pub name: Name,
|
||||||
|
/// The type bound to this associated type (in `Item = T`, this would be the
|
||||||
|
/// `T`). This can be `None` if there are bounds instead.
|
||||||
|
pub type_ref: Option<TypeRef>,
|
||||||
|
/// Bounds for the associated type, like in `Iterator<Item:
|
||||||
|
/// SomeOtherTrait>`. (This is the unstable `associated_type_bounds`
|
||||||
|
/// feature.)
|
||||||
|
pub bounds: Vec<TypeBound>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single generic argument.
|
/// A single generic argument.
|
||||||
|
|
|
@ -9,11 +9,12 @@ use hir_expand::{
|
||||||
hygiene::Hygiene,
|
hygiene::Hygiene,
|
||||||
name::{name, AsName},
|
name::{name, AsName},
|
||||||
};
|
};
|
||||||
use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner};
|
use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner, TypeBoundsOwner};
|
||||||
|
|
||||||
|
use super::AssociatedTypeBinding;
|
||||||
use crate::{
|
use crate::{
|
||||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
|
path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
|
||||||
type_ref::TypeRef,
|
type_ref::{TypeBound, TypeRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) use lower_use::lower_use_tree;
|
pub(super) use lower_use::lower_use_tree;
|
||||||
|
@ -136,10 +137,16 @@ pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option<GenericArgs>
|
||||||
// lifetimes ignored for now
|
// lifetimes ignored for now
|
||||||
let mut bindings = Vec::new();
|
let mut bindings = Vec::new();
|
||||||
for assoc_type_arg in node.assoc_type_args() {
|
for assoc_type_arg in node.assoc_type_args() {
|
||||||
|
let assoc_type_arg: ast::AssocTypeArg = assoc_type_arg;
|
||||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||||
let name = name_ref.as_name();
|
let name = name_ref.as_name();
|
||||||
let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
|
let type_ref = assoc_type_arg.type_ref().map(TypeRef::from_ast);
|
||||||
bindings.push((name, type_ref));
|
let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
|
||||||
|
l.bounds().map(TypeBound::from_ast).collect()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if args.is_empty() && bindings.is_empty() {
|
if args.is_empty() && bindings.is_empty() {
|
||||||
|
@ -168,7 +175,11 @@ fn lower_generic_args_from_fn_path(
|
||||||
}
|
}
|
||||||
if let Some(ret_type) = ret_type {
|
if let Some(ret_type) = ret_type {
|
||||||
let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
|
let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
|
||||||
bindings.push((name![Output], type_ref))
|
bindings.push(AssociatedTypeBinding {
|
||||||
|
name: name![Output],
|
||||||
|
type_ref: Some(type_ref),
|
||||||
|
bounds: Vec::new(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if args.is_empty() && bindings.is_empty() {
|
if args.is_empty() && bindings.is_empty() {
|
||||||
None
|
None
|
||||||
|
|
|
@ -163,8 +163,16 @@ impl TypeRef {
|
||||||
let crate::path::GenericArg::Type(type_ref) = arg;
|
let crate::path::GenericArg::Type(type_ref) = arg;
|
||||||
go(type_ref, f);
|
go(type_ref, f);
|
||||||
}
|
}
|
||||||
for (_, type_ref) in &args_and_bindings.bindings {
|
for binding in &args_and_bindings.bindings {
|
||||||
go(type_ref, f);
|
if let Some(type_ref) = &binding.type_ref {
|
||||||
|
go(type_ref, f);
|
||||||
|
}
|
||||||
|
for bound in &binding.bounds {
|
||||||
|
match bound {
|
||||||
|
TypeBound::Path(path) => go_path(path, f),
|
||||||
|
TypeBound::Error => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ authors = ["rust-analyzer developers"]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
itertools = "0.9.0"
|
||||||
arrayvec = "0.5.1"
|
arrayvec = "0.5.1"
|
||||||
smallvec = "1.2.0"
|
smallvec = "1.2.0"
|
||||||
ena = "0.13.1"
|
ena = "0.13.1"
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
adt::StructKind,
|
adt::StructKind,
|
||||||
builtin_type::BuiltinType,
|
builtin_type::BuiltinType,
|
||||||
|
@ -360,13 +362,23 @@ impl Ty {
|
||||||
},
|
},
|
||||||
Some(TypeNs::GenericParam(param_id)) => {
|
Some(TypeNs::GenericParam(param_id)) => {
|
||||||
let predicates = ctx.db.generic_predicates_for_param(param_id);
|
let predicates = ctx.db.generic_predicates_for_param(param_id);
|
||||||
predicates
|
let mut traits_: Vec<_> = predicates
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|pred| match &pred.value {
|
.filter_map(|pred| match &pred.value {
|
||||||
GenericPredicate::Implemented(tr) => Some(tr.trait_),
|
GenericPredicate::Implemented(tr) => Some(tr.trait_),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect();
|
||||||
|
// Handle `Self::Type` referring to own associated type in trait definitions
|
||||||
|
if let GenericDefId::TraitId(trait_id) = param_id.parent {
|
||||||
|
let generics = generics(ctx.db.upcast(), trait_id.into());
|
||||||
|
if generics.params.types[param_id.local_id].provenance
|
||||||
|
== TypeParamProvenance::TraitSelf
|
||||||
|
{
|
||||||
|
traits_.push(trait_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traits_
|
||||||
}
|
}
|
||||||
_ => return Ty::Unknown,
|
_ => return Ty::Unknown,
|
||||||
};
|
};
|
||||||
|
@ -596,21 +608,35 @@ fn assoc_type_bindings_from_type_bound<'a>(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|segment| segment.args_and_bindings.into_iter())
|
.flat_map(|segment| segment.args_and_bindings.into_iter())
|
||||||
.flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
|
.flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
|
||||||
.map(move |(name, type_ref)| {
|
.flat_map(move |binding| {
|
||||||
let associated_ty = associated_type_by_name_including_super_traits(
|
let associated_ty = associated_type_by_name_including_super_traits(
|
||||||
ctx.db.upcast(),
|
ctx.db.upcast(),
|
||||||
trait_ref.trait_,
|
trait_ref.trait_,
|
||||||
&name,
|
&binding.name,
|
||||||
);
|
);
|
||||||
let associated_ty = match associated_ty {
|
let associated_ty = match associated_ty {
|
||||||
None => return GenericPredicate::Error,
|
None => return SmallVec::<[GenericPredicate; 1]>::new(),
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
};
|
};
|
||||||
let projection_ty =
|
let projection_ty =
|
||||||
ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() };
|
ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() };
|
||||||
let ty = Ty::from_hir(ctx, type_ref);
|
let mut preds = SmallVec::with_capacity(
|
||||||
let projection_predicate = ProjectionPredicate { projection_ty, ty };
|
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
|
||||||
GenericPredicate::Projection(projection_predicate)
|
);
|
||||||
|
if let Some(type_ref) = &binding.type_ref {
|
||||||
|
let ty = Ty::from_hir(ctx, type_ref);
|
||||||
|
let projection_predicate =
|
||||||
|
ProjectionPredicate { projection_ty: projection_ty.clone(), ty };
|
||||||
|
preds.push(GenericPredicate::Projection(projection_predicate));
|
||||||
|
}
|
||||||
|
for bound in &binding.bounds {
|
||||||
|
preds.extend(GenericPredicate::from_type_bound(
|
||||||
|
ctx,
|
||||||
|
bound,
|
||||||
|
Ty::Projection(projection_ty.clone()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
preds
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -451,8 +451,7 @@ pub mod str {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
// should be Option<char>, but currently not because of Chalk ambiguity problem
|
assert_eq!("(Option<char>, Option<char>)", super::type_at_pos(&db, pos));
|
||||||
assert_eq!("(Option<{unknown}>, Option<{unknown}>)", super::type_at_pos(&db, pos));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1803,7 +1803,7 @@ fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unselected_projection_on_trait_self() {
|
fn unselected_projection_on_impl_self() {
|
||||||
assert_snapshot!(infer(
|
assert_snapshot!(infer(
|
||||||
r#"
|
r#"
|
||||||
//- /main.rs
|
//- /main.rs
|
||||||
|
@ -1843,6 +1843,30 @@ impl Trait for S2 {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unselected_projection_on_trait_self() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Trait {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn f(&self) -> Self::Item { loop {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
impl Trait for S {
|
||||||
|
type Item = u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
S.f()<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "u32");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trait_impl_self_ty() {
|
fn trait_impl_self_ty() {
|
||||||
let t = type_at(
|
let t = type_at(
|
||||||
|
@ -1923,6 +1947,53 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
|
||||||
assert_eq!(t, "{unknown}");
|
assert_eq!(t, "{unknown}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_assoc_type_bounds_1() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Iterator {
|
||||||
|
type Item;
|
||||||
|
}
|
||||||
|
trait OtherTrait<T> {
|
||||||
|
fn foo(&self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// workaround for Chalk assoc type normalization problems
|
||||||
|
pub struct S<T>;
|
||||||
|
impl<T: Iterator> Iterator for S<T> {
|
||||||
|
type Item = <T as Iterator>::Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test<I: Iterator<Item: OtherTrait<u32>>>() {
|
||||||
|
let x: <S<I> as Iterator>::Item;
|
||||||
|
x.foo()<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "u32");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_assoc_type_bounds_2() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
trait Iterator {
|
||||||
|
type Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
|
||||||
|
let x: <<I as Iterator>::Item as Iterator>::Item;
|
||||||
|
x<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
// assert_eq!(t, "u32");
|
||||||
|
// doesn't currently work, Chalk #234
|
||||||
|
assert_eq!(t, "{unknown}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unify_impl_trait() {
|
fn unify_impl_trait() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName};
|
use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use super::{from_chalk, Interner};
|
use super::{from_chalk, Interner};
|
||||||
use crate::{db::HirDatabase, CallableDef, TypeCtor};
|
use crate::{db::HirDatabase, CallableDef, TypeCtor};
|
||||||
use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId};
|
use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId};
|
||||||
|
|
||||||
pub use unsafe_tls::{set_current_program, with_current_program};
|
pub use unsafe_tls::{set_current_program, with_current_program};
|
||||||
|
|
||||||
|
@ -69,7 +70,27 @@ impl DebugContext<'_> {
|
||||||
write!(f, "{}::{}", trait_name, name)?;
|
write!(f, "{}::{}", trait_name, name)?;
|
||||||
}
|
}
|
||||||
TypeCtor::Closure { def, expr } => {
|
TypeCtor::Closure { def, expr } => {
|
||||||
write!(f, "{{closure {:?} in {:?}}}", expr.into_raw(), def)?;
|
write!(f, "{{closure {:?} in ", expr.into_raw())?;
|
||||||
|
match def {
|
||||||
|
DefWithBodyId::FunctionId(func) => {
|
||||||
|
write!(f, "fn {}", self.0.function_data(func).name)?
|
||||||
|
}
|
||||||
|
DefWithBodyId::StaticId(s) => {
|
||||||
|
if let Some(name) = self.0.static_data(s).name.as_ref() {
|
||||||
|
write!(f, "body of static {}", name)?;
|
||||||
|
} else {
|
||||||
|
write!(f, "body of unnamed static {:?}", s)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DefWithBodyId::ConstId(c) => {
|
||||||
|
if let Some(name) = self.0.const_data(c).name.as_ref() {
|
||||||
|
write!(f, "body of const {}", name)?;
|
||||||
|
} else {
|
||||||
|
write!(f, "body of unnamed const {:?}", c)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
write!(f, "}}")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -113,14 +134,15 @@ impl DebugContext<'_> {
|
||||||
};
|
};
|
||||||
let trait_data = self.0.trait_data(trait_);
|
let trait_data = self.0.trait_data(trait_);
|
||||||
let params = alias.substitution.parameters(&Interner);
|
let params = alias.substitution.parameters(&Interner);
|
||||||
write!(
|
write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?;
|
||||||
fmt,
|
if params.len() > 1 {
|
||||||
"<{:?} as {}<{:?}>>::{}",
|
write!(
|
||||||
¶ms[0],
|
fmt,
|
||||||
trait_data.name,
|
"<{}>",
|
||||||
¶ms[1..],
|
¶ms[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
|
||||||
type_alias_data.name
|
)?;
|
||||||
)
|
}
|
||||||
|
write!(fmt, ">::{}", type_alias_data.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_ty(
|
pub fn debug_ty(
|
||||||
|
|
Loading…
Reference in a new issue