Skip to content

Commit 1e0ba04

Browse files
bors[bot]flodieboldedwin0cheng
authored
Merge #3966 #3968
3966: Add support for bounds on associated types in trait definitions r=matklad a=flodiebold E.g. ```rust trait Trait { type Item: SomeOtherTrait; } ``` Note that these don't simply desugar to where clauses; as I understand it, where clauses have to be proved by the *user* of the trait, but these bounds are proved by the *implementor*. (Also, where clauses on associated types are unstable.) (Another one from my recursive solver branch...) 3968: Remove format from syntax_bridge hot path r=matklad a=edwin0cheng Although only around 1% speed up by running: ``` Measure-Command {start-process .\target\release\rust-analyzer "analysis-stats -q ." -NoNewWindow -wait} ``` Co-authored-by: Florian Diebold <[email protected]> Co-authored-by: Edwin Cheng <[email protected]>
3 parents d61909f + c8b2ec8 + 464af68 commit 1e0ba04

File tree

5 files changed

+119
-16
lines changed

5 files changed

+119
-16
lines changed

crates/ra_hir_def/src/data.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use hir_expand::{
99
};
1010
use ra_prof::profile;
1111
use ra_syntax::ast::{
12-
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner,
12+
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
13+
VisibilityOwner,
1314
};
1415

1516
use crate::{
@@ -110,6 +111,7 @@ pub struct TypeAliasData {
110111
pub name: Name,
111112
pub type_ref: Option<TypeRef>,
112113
pub visibility: RawVisibility,
114+
pub bounds: Vec<TypeBound>,
113115
}
114116

115117
impl TypeAliasData {
@@ -122,9 +124,17 @@ impl TypeAliasData {
122124
let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
123125
let type_ref = node.value.type_ref().map(TypeRef::from_ast);
124126
let vis_default = RawVisibility::default_for_container(loc.container);
125-
let visibility =
126-
RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
127-
Arc::new(TypeAliasData { name, type_ref, visibility })
127+
let visibility = RawVisibility::from_ast_with_default(
128+
db,
129+
vis_default,
130+
node.as_ref().map(|n| n.visibility()),
131+
);
132+
let bounds = if let Some(bound_list) = node.value.type_bound_list() {
133+
bound_list.bounds().map(TypeBound::from_ast).collect()
134+
} else {
135+
Vec::new()
136+
};
137+
Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
128138
}
129139
}
130140

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,33 @@ fn main() {
20932093
);
20942094
}
20952095

2096+
#[test]
2097+
fn associated_type_bound() {
2098+
let t = type_at(
2099+
r#"
2100+
//- /main.rs
2101+
pub trait Trait {
2102+
type Item: OtherTrait<u32>;
2103+
}
2104+
pub trait OtherTrait<T> {
2105+
fn foo(&self) -> T;
2106+
}
2107+
2108+
// this is just a workaround for chalk#234
2109+
pub struct S<T>;
2110+
impl<T: Trait> Trait for S<T> {
2111+
type Item = <T as Trait>::Item;
2112+
}
2113+
2114+
fn test<T: Trait>() {
2115+
let y: <S<T> as Trait>::Item = no_matter;
2116+
y.foo()<|>;
2117+
}
2118+
"#,
2119+
);
2120+
assert_eq!(t, "u32");
2121+
}
2122+
20962123
#[test]
20972124
fn dyn_trait_through_chalk() {
20982125
let t = type_at(

crates/ra_hir_ty/src/traits.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,16 @@ fn solve(
194194
}
195195
remaining > 0
196196
};
197-
let mut solve = || solver.solve_limited(&context, goal, should_continue);
197+
let mut solve = || {
198+
let solution = solver.solve_limited(&context, goal, should_continue);
199+
log::debug!("solve({:?}) => {:?}", goal, solution);
200+
solution
201+
};
198202
// don't set the TLS for Chalk unless Chalk debugging is active, to make
199203
// extra sure we only use it for debugging
200204
let solution =
201205
if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() };
202206

203-
log::debug!("solve({:?}) => {:?}", goal, solution);
204207
solution
205208
}
206209

crates/ra_hir_ty/src/traits/chalk.rs

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,55 @@ fn convert_where_clauses(
673673
result
674674
}
675675

676+
fn generic_predicate_to_inline_bound(
677+
db: &dyn HirDatabase,
678+
pred: &GenericPredicate,
679+
self_ty: &Ty,
680+
) -> Option<chalk_rust_ir::InlineBound<Interner>> {
681+
// An InlineBound is like a GenericPredicate, except the self type is left out.
682+
// We don't have a special type for this, but Chalk does.
683+
match pred {
684+
GenericPredicate::Implemented(trait_ref) => {
685+
if &trait_ref.substs[0] != self_ty {
686+
// we can only convert predicates back to type bounds if they
687+
// have the expected self type
688+
return None;
689+
}
690+
let args_no_self = trait_ref.substs[1..]
691+
.iter()
692+
.map(|ty| ty.clone().to_chalk(db).cast(&Interner))
693+
.collect();
694+
let trait_bound =
695+
chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
696+
Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
697+
}
698+
GenericPredicate::Projection(proj) => {
699+
if &proj.projection_ty.parameters[0] != self_ty {
700+
return None;
701+
}
702+
let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
703+
AssocContainerId::TraitId(t) => t,
704+
_ => panic!("associated type not in trait"),
705+
};
706+
let args_no_self = proj.projection_ty.parameters[1..]
707+
.iter()
708+
.map(|ty| ty.clone().to_chalk(db).cast(&Interner))
709+
.collect();
710+
let alias_eq_bound = chalk_rust_ir::AliasEqBound {
711+
value: proj.ty.clone().to_chalk(db),
712+
trait_bound: chalk_rust_ir::TraitBound {
713+
trait_id: trait_.to_chalk(db),
714+
args_no_self,
715+
},
716+
associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
717+
parameters: Vec::new(), // FIXME we don't support generic associated types yet
718+
};
719+
Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
720+
}
721+
GenericPredicate::Error => None,
722+
}
723+
}
724+
676725
impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
677726
fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
678727
self.db.associated_ty_data(id)
@@ -761,12 +810,25 @@ pub(crate) fn associated_ty_data_query(
761810
AssocContainerId::TraitId(t) => t,
762811
_ => panic!("associated type not in trait"),
763812
};
813+
814+
// Lower bounds -- we could/should maybe move this to a separate query in `lower`
815+
let type_alias_data = db.type_alias_data(type_alias);
764816
let generic_params = generics(db.upcast(), type_alias.into());
765-
let bound_data = chalk_rust_ir::AssociatedTyDatumBound {
766-
// FIXME add bounds and where clauses
767-
bounds: vec![],
768-
where_clauses: vec![],
769-
};
817+
let bound_vars = Substs::bound_vars(&generic_params);
818+
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
819+
let ctx = crate::TyLoweringContext::new(db, &resolver)
820+
.with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
821+
let self_ty = Ty::Bound(crate::BoundVar::new(crate::DebruijnIndex::INNERMOST, 0));
822+
let bounds = type_alias_data
823+
.bounds
824+
.iter()
825+
.flat_map(|bound| GenericPredicate::from_type_bound(&ctx, bound, self_ty.clone()))
826+
.filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty))
827+
.map(|bound| make_binders(bound.shifted_in(&Interner), 0))
828+
.collect();
829+
830+
let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
831+
let bound_data = chalk_rust_ir::AssociatedTyDatumBound { bounds, where_clauses };
770832
let datum = AssociatedTyDatum {
771833
trait_id: trait_.to_chalk(db),
772834
id,

crates/ra_mbe/src/syntax_bridge.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -607,12 +607,13 @@ impl<'a> TreeSink for TtTreeSink<'a> {
607607
let text: SmolStr = match self.cursor.token_tree() {
608608
Some(tt::TokenTree::Leaf(leaf)) => {
609609
// Mark the range if needed
610-
let id = match leaf {
611-
tt::Leaf::Ident(ident) => ident.id,
612-
tt::Leaf::Punct(punct) => punct.id,
613-
tt::Leaf::Literal(lit) => lit.id,
610+
let (text, id) = match leaf {
611+
tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id),
612+
tt::Leaf::Punct(punct) => {
613+
(SmolStr::new_inline_from_ascii(1, &[punct.char as u8]), punct.id)
614+
}
615+
tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id),
614616
};
615-
let text = SmolStr::new(format!("{}", leaf));
616617
let range = TextRange::offset_len(self.text_pos, TextUnit::of_str(&text));
617618
self.token_map.insert(id, range);
618619
self.cursor = self.cursor.bump();

0 commit comments

Comments
 (0)