diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-02-09 11:35:08 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-02-09 11:35:08 +0000 |
commit | 01836a0f35fa163025c64cabe1d0c34bb4f69c92 (patch) | |
tree | aa1a3cf97173b2885f8b6d23002c73196f9a0b61 /crates/ra_hir_ty/src/lib.rs | |
parent | 961a69b88f923d4477ca4f746a793217a0cc8576 (diff) | |
parent | eefe02ce6e1750b771cf99125429358e87485745 (diff) |
Merge #3050
3050: Refactor type parameters, implement argument position impl trait r=matklad a=flodiebold
I wanted to implement APIT by lowering to type parameters because we need to do that anyway for correctness and don't need Chalk support for it; this grew into some more wide-ranging refactoring of how type parameters are handled :sweat_smile:
- use Ty::Bound instead of Ty::Param to represent polymorphism, and explicitly
count binders. This gets us closer to Chalk's way of doing things, and means
that we now only use Param as a placeholder for an unknown type, e.g. within
a generic function. I.e. we're never using Param in a situation where we want
to substitute it, and the method to do that is gone; `subst` now always works
on bound variables. (This changes how the types of generic functions print;
previously, you'd get something like `fn identity<i32>(T) -> T`, but now we
display the substituted signature `fn identity<i32>(i32) -> i32`, which I think
makes more sense.)
- once we do this, it's more natural to represent `Param` by a globally unique
ID; the use of indices was mostly to make substituting easier. This also
means we fix the bug where `Param` loses its name when going through Chalk.
- I would actually like to rename `Param` to `Placeholder` to better reflect its use and
get closer to Chalk, but I'll leave that to a follow-up.
- introduce a context for type lowering, to allow lowering `impl Trait` to
different things depending on where we are. And since we have that, we can
also lower type parameters directly to variables instead of placeholders.
Also, we'll be able to use this later to collect diagnostics.
- implement argument position impl trait by lowering it to type parameters.
I've realized that this is necessary to correctly implement it; e.g. consider
`fn foo(impl Display) -> impl Something`. It's observable that the return
type of e.g. `foo(1u32)` unifies with itself, but doesn't unify with e.g.
`foo(1i32)`; so the return type needs to be parameterized by the argument
type.
This fixes a few bugs as well:
- type parameters 'losing' their name when they go through Chalk, as mentioned
above (i.e. getting `[missing name]` somewhere)
- impl trait not being considered as implementing the super traits (very
noticeable for the `db` in RA)
- the fact that argument impl trait was only turned into variables when the
function got called caused type mismatches when the function was used as a
value (fixes a few type mismatches in RA)
The one thing I'm not so happy with here is how we're lowering `impl Trait` types to variables; since `TypeRef`s don't have an identity currently, we just count how many of them we have seen while going through the function signature. That's quite fragile though, since we have to do it while desugaring generics and while lowering the type signature, and in the exact same order in both cases. We could consider either giving only `TypeRef::ImplTrait` a local id, or maybe just giving all `TypeRef`s an identity after all (we talked about this before)...
Follow-up tasks:
- handle return position impl trait; we basically need to create a variable and some trait obligations for that variable
- rename `Param` to `Placeholder`
Co-authored-by: Florian Diebold <[email protected]>
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir_ty/src/lib.rs')
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 262 |
1 files changed, 154 insertions, 108 deletions
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 08d501ccd..c5fe18c85 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -44,8 +44,8 @@ use std::sync::Arc; | |||
44 | use std::{fmt, iter, mem}; | 44 | use std::{fmt, iter, mem}; |
45 | 45 | ||
46 | use hir_def::{ | 46 | use hir_def::{ |
47 | expr::ExprId, type_ref::Mutability, AdtId, AssocContainerId, DefWithBodyId, GenericDefId, | 47 | expr::ExprId, generics::TypeParamProvenance, type_ref::Mutability, AdtId, AssocContainerId, |
48 | HasModule, Lookup, TraitId, TypeAliasId, | 48 | DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, |
49 | }; | 49 | }; |
50 | use hir_expand::name::Name; | 50 | use hir_expand::name::Name; |
51 | use ra_db::{impl_intern_key, salsa, CrateId}; | 51 | use ra_db::{impl_intern_key, salsa, CrateId}; |
@@ -60,7 +60,9 @@ use display::{HirDisplay, HirFormatter}; | |||
60 | pub use autoderef::autoderef; | 60 | pub use autoderef::autoderef; |
61 | pub use infer::{do_infer_query, InferTy, InferenceResult}; | 61 | pub use infer::{do_infer_query, InferTy, InferenceResult}; |
62 | pub use lower::CallableDef; | 62 | pub use lower::CallableDef; |
63 | pub use lower::{callable_item_sig, TyDefId, ValueTyDefId}; | 63 | pub use lower::{ |
64 | callable_item_sig, ImplTraitLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId, | ||
65 | }; | ||
64 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; | 66 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; |
65 | 67 | ||
66 | /// A type constructor or type name: this might be something like the primitive | 68 | /// A type constructor or type name: this might be something like the primitive |
@@ -285,22 +287,20 @@ pub enum Ty { | |||
285 | /// trait and all its parameters are fully known. | 287 | /// trait and all its parameters are fully known. |
286 | Projection(ProjectionTy), | 288 | Projection(ProjectionTy), |
287 | 289 | ||
288 | /// A type parameter; for example, `T` in `fn f<T>(x: T) {} | 290 | /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T) |
289 | Param { | 291 | /// {}` when we're type-checking the body of that function. In this |
290 | /// The index of the parameter (starting with parameters from the | 292 | /// situation, we know this stands for *some* type, but don't know the exact |
291 | /// surrounding impl, then the current function). | 293 | /// type. |
292 | idx: u32, | 294 | Param(TypeParamId), |
293 | /// The name of the parameter, for displaying. | 295 | |
294 | // FIXME get rid of this | 296 | /// A bound type variable. This is used in various places: when representing |
295 | name: Name, | 297 | /// some polymorphic type like the type of function `fn f<T>`, the type |
296 | }, | 298 | /// parameters get turned into variables; during trait resolution, inference |
297 | 299 | /// variables get turned into bound variables and back; and in `Dyn` the | |
298 | /// A bound type variable. Used during trait resolution to represent Chalk | 300 | /// `Self` type is represented with a bound variable as well. |
299 | /// variables, and in `Dyn` and `Opaque` bounds to represent the `Self` type. | ||
300 | Bound(u32), | 301 | Bound(u32), |
301 | 302 | ||
302 | /// A type variable used during type checking. Not to be confused with a | 303 | /// A type variable used during type checking. |
303 | /// type parameter. | ||
304 | Infer(InferTy), | 304 | Infer(InferTy), |
305 | 305 | ||
306 | /// A trait object (`dyn Trait` or bare `Trait` in pre-2018 Rust). | 306 | /// A trait object (`dyn Trait` or bare `Trait` in pre-2018 Rust). |
@@ -364,15 +364,19 @@ impl Substs { | |||
364 | } | 364 | } |
365 | 365 | ||
366 | /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). | 366 | /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). |
367 | pub(crate) fn identity(generic_params: &Generics) -> Substs { | 367 | pub(crate) fn type_params_for_generics(generic_params: &Generics) -> Substs { |
368 | Substs( | 368 | Substs(generic_params.iter().map(|(id, _)| Ty::Param(id)).collect()) |
369 | generic_params.iter().map(|(idx, p)| Ty::Param { idx, name: p.name.clone() }).collect(), | 369 | } |
370 | ) | 370 | |
371 | /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). | ||
372 | pub fn type_params(db: &impl HirDatabase, def: impl Into<GenericDefId>) -> Substs { | ||
373 | let params = generics(db, def.into()); | ||
374 | Substs::type_params_for_generics(¶ms) | ||
371 | } | 375 | } |
372 | 376 | ||
373 | /// Return Substs that replace each parameter by a bound variable. | 377 | /// Return Substs that replace each parameter by a bound variable. |
374 | pub(crate) fn bound_vars(generic_params: &Generics) -> Substs { | 378 | pub(crate) fn bound_vars(generic_params: &Generics) -> Substs { |
375 | Substs(generic_params.iter().map(|(idx, _p)| Ty::Bound(idx)).collect()) | 379 | Substs(generic_params.iter().enumerate().map(|(idx, _)| Ty::Bound(idx as u32)).collect()) |
376 | } | 380 | } |
377 | 381 | ||
378 | pub fn build_for_def(db: &impl HirDatabase, def: impl Into<GenericDefId>) -> SubstsBuilder { | 382 | pub fn build_for_def(db: &impl HirDatabase, def: impl Into<GenericDefId>) -> SubstsBuilder { |
@@ -420,11 +424,6 @@ impl SubstsBuilder { | |||
420 | self.fill((starting_from..).map(Ty::Bound)) | 424 | self.fill((starting_from..).map(Ty::Bound)) |
421 | } | 425 | } |
422 | 426 | ||
423 | pub fn fill_with_params(self) -> Self { | ||
424 | let start = self.vec.len() as u32; | ||
425 | self.fill((start..).map(|idx| Ty::Param { idx, name: Name::missing() })) | ||
426 | } | ||
427 | |||
428 | pub fn fill_with_unknown(self) -> Self { | 427 | pub fn fill_with_unknown(self) -> Self { |
429 | self.fill(iter::repeat(Ty::Unknown)) | 428 | self.fill(iter::repeat(Ty::Unknown)) |
430 | } | 429 | } |
@@ -451,6 +450,32 @@ impl Deref for Substs { | |||
451 | } | 450 | } |
452 | } | 451 | } |
453 | 452 | ||
453 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||
454 | pub struct Binders<T> { | ||
455 | pub num_binders: usize, | ||
456 | pub value: T, | ||
457 | } | ||
458 | |||
459 | impl<T> Binders<T> { | ||
460 | pub fn new(num_binders: usize, value: T) -> Self { | ||
461 | Self { num_binders, value } | ||
462 | } | ||
463 | } | ||
464 | |||
465 | impl<T: TypeWalk> Binders<T> { | ||
466 | /// Substitutes all variables. | ||
467 | pub fn subst(self, subst: &Substs) -> T { | ||
468 | assert_eq!(subst.len(), self.num_binders); | ||
469 | self.value.subst_bound_vars(subst) | ||
470 | } | ||
471 | |||
472 | /// Substitutes just a prefix of the variables (shifting the rest). | ||
473 | pub fn subst_prefix(self, subst: &Substs) -> Binders<T> { | ||
474 | assert!(subst.len() < self.num_binders); | ||
475 | Binders::new(self.num_binders - subst.len(), self.value.subst_bound_vars(subst)) | ||
476 | } | ||
477 | } | ||
478 | |||
454 | /// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. | 479 | /// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. |
455 | /// Name to be bikeshedded: TraitBound? TraitImplements? | 480 | /// Name to be bikeshedded: TraitBound? TraitImplements? |
456 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 481 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
@@ -551,6 +576,9 @@ pub struct FnSig { | |||
551 | params_and_return: Arc<[Ty]>, | 576 | params_and_return: Arc<[Ty]>, |
552 | } | 577 | } |
553 | 578 | ||
579 | /// A polymorphic function signature. | ||
580 | pub type PolyFnSig = Binders<FnSig>; | ||
581 | |||
554 | impl FnSig { | 582 | impl FnSig { |
555 | pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty) -> FnSig { | 583 | pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty) -> FnSig { |
556 | params.push(ret); | 584 | params.push(ret); |
@@ -730,22 +758,7 @@ pub trait TypeWalk { | |||
730 | self | 758 | self |
731 | } | 759 | } |
732 | 760 | ||
733 | /// Replaces type parameters in this type using the given `Substs`. (So e.g. | 761 | /// Substitutes `Ty::Bound` vars with the given substitution. |
734 | /// if `self` is `&[T]`, where type parameter T has index 0, and the | ||
735 | /// `Substs` contain `u32` at index 0, we'll have `&[u32]` afterwards.) | ||
736 | fn subst(self, substs: &Substs) -> Self | ||
737 | where | ||
738 | Self: Sized, | ||
739 | { | ||
740 | self.fold(&mut |ty| match ty { | ||
741 | Ty::Param { idx, name } => { | ||
742 | substs.get(idx as usize).cloned().unwrap_or(Ty::Param { idx, name }) | ||
743 | } | ||
744 | ty => ty, | ||
745 | }) | ||
746 | } | ||
747 | |||
748 | /// Substitutes `Ty::Bound` vars (as opposed to type parameters). | ||
749 | fn subst_bound_vars(mut self, substs: &Substs) -> Self | 762 | fn subst_bound_vars(mut self, substs: &Substs) -> Self |
750 | where | 763 | where |
751 | Self: Sized, | 764 | Self: Sized, |
@@ -755,6 +768,9 @@ pub trait TypeWalk { | |||
755 | &mut Ty::Bound(idx) => { | 768 | &mut Ty::Bound(idx) => { |
756 | if idx as usize >= binders && (idx as usize - binders) < substs.len() { | 769 | if idx as usize >= binders && (idx as usize - binders) < substs.len() { |
757 | *ty = substs.0[idx as usize - binders].clone(); | 770 | *ty = substs.0[idx as usize - binders].clone(); |
771 | } else if idx as usize >= binders + substs.len() { | ||
772 | // shift free binders | ||
773 | *ty = Ty::Bound(idx - substs.len() as u32); | ||
758 | } | 774 | } |
759 | } | 775 | } |
760 | _ => {} | 776 | _ => {} |
@@ -880,7 +896,7 @@ impl HirDisplay for ApplicationTy { | |||
880 | write!(f, ") -> {}", sig.ret().display(f.db))?; | 896 | write!(f, ") -> {}", sig.ret().display(f.db))?; |
881 | } | 897 | } |
882 | TypeCtor::FnDef(def) => { | 898 | TypeCtor::FnDef(def) => { |
883 | let sig = f.db.callable_item_signature(def); | 899 | let sig = f.db.callable_item_signature(def).subst(&self.parameters); |
884 | let name = match def { | 900 | let name = match def { |
885 | CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), | 901 | CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), |
886 | CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), | 902 | CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), |
@@ -896,9 +912,16 @@ impl HirDisplay for ApplicationTy { | |||
896 | } | 912 | } |
897 | } | 913 | } |
898 | if self.parameters.len() > 0 { | 914 | if self.parameters.len() > 0 { |
899 | write!(f, "<")?; | 915 | let generics = generics(f.db, def.into()); |
900 | f.write_joined(&*self.parameters.0, ", ")?; | 916 | let (parent_params, self_param, type_params, _impl_trait_params) = |
901 | write!(f, ">")?; | 917 | generics.provenance_split(); |
918 | let total_len = parent_params + self_param + type_params; | ||
919 | // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? | ||
920 | if total_len > 0 { | ||
921 | write!(f, "<")?; | ||
922 | f.write_joined(&self.parameters.0[..total_len], ", ")?; | ||
923 | write!(f, ">")?; | ||
924 | } | ||
902 | } | 925 | } |
903 | write!(f, "(")?; | 926 | write!(f, "(")?; |
904 | f.write_joined(sig.params(), ", ")?; | 927 | f.write_joined(sig.params(), ", ")?; |
@@ -1009,7 +1032,24 @@ impl HirDisplay for Ty { | |||
1009 | match self { | 1032 | match self { |
1010 | Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, | 1033 | Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, |
1011 | Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, | 1034 | Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, |
1012 | Ty::Param { name, .. } => write!(f, "{}", name)?, | 1035 | Ty::Param(id) => { |
1036 | let generics = generics(f.db, id.parent); | ||
1037 | let param_data = &generics.params.types[id.local_id]; | ||
1038 | match param_data.provenance { | ||
1039 | TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { | ||
1040 | write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? | ||
1041 | } | ||
1042 | TypeParamProvenance::ArgumentImplTrait => { | ||
1043 | write!(f, "impl ")?; | ||
1044 | let bounds = f.db.generic_predicates_for_param(*id); | ||
1045 | let substs = Substs::type_params_for_generics(&generics); | ||
1046 | write_bounds_like_dyn_trait( | ||
1047 | &bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(), | ||
1048 | f, | ||
1049 | )?; | ||
1050 | } | ||
1051 | } | ||
1052 | } | ||
1013 | Ty::Bound(idx) => write!(f, "?{}", idx)?, | 1053 | Ty::Bound(idx) => write!(f, "?{}", idx)?, |
1014 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | 1054 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { |
1015 | match self { | 1055 | match self { |
@@ -1017,66 +1057,7 @@ impl HirDisplay for Ty { | |||
1017 | Ty::Opaque(_) => write!(f, "impl ")?, | 1057 | Ty::Opaque(_) => write!(f, "impl ")?, |
1018 | _ => unreachable!(), | 1058 | _ => unreachable!(), |
1019 | }; | 1059 | }; |
1020 | // Note: This code is written to produce nice results (i.e. | 1060 | write_bounds_like_dyn_trait(&predicates, f)?; |
1021 | // corresponding to surface Rust) for types that can occur in | ||
1022 | // actual Rust. It will have weird results if the predicates | ||
1023 | // aren't as expected (i.e. self types = $0, projection | ||
1024 | // predicates for a certain trait come after the Implemented | ||
1025 | // predicate for that trait). | ||
1026 | let mut first = true; | ||
1027 | let mut angle_open = false; | ||
1028 | for p in predicates.iter() { | ||
1029 | match p { | ||
1030 | GenericPredicate::Implemented(trait_ref) => { | ||
1031 | if angle_open { | ||
1032 | write!(f, ">")?; | ||
1033 | } | ||
1034 | if !first { | ||
1035 | write!(f, " + ")?; | ||
1036 | } | ||
1037 | // We assume that the self type is $0 (i.e. the | ||
1038 | // existential) here, which is the only thing that's | ||
1039 | // possible in actual Rust, and hence don't print it | ||
1040 | write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; | ||
1041 | if trait_ref.substs.len() > 1 { | ||
1042 | write!(f, "<")?; | ||
1043 | f.write_joined(&trait_ref.substs[1..], ", ")?; | ||
1044 | // there might be assoc type bindings, so we leave the angle brackets open | ||
1045 | angle_open = true; | ||
1046 | } | ||
1047 | } | ||
1048 | GenericPredicate::Projection(projection_pred) => { | ||
1049 | // in types in actual Rust, these will always come | ||
1050 | // after the corresponding Implemented predicate | ||
1051 | if angle_open { | ||
1052 | write!(f, ", ")?; | ||
1053 | } else { | ||
1054 | write!(f, "<")?; | ||
1055 | angle_open = true; | ||
1056 | } | ||
1057 | let name = | ||
1058 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty) | ||
1059 | .name | ||
1060 | .clone(); | ||
1061 | write!(f, "{} = ", name)?; | ||
1062 | projection_pred.ty.hir_fmt(f)?; | ||
1063 | } | ||
1064 | GenericPredicate::Error => { | ||
1065 | if angle_open { | ||
1066 | // impl Trait<X, {error}> | ||
1067 | write!(f, ", ")?; | ||
1068 | } else if !first { | ||
1069 | // impl Trait + {error} | ||
1070 | write!(f, " + ")?; | ||
1071 | } | ||
1072 | p.hir_fmt(f)?; | ||
1073 | } | ||
1074 | } | ||
1075 | first = false; | ||
1076 | } | ||
1077 | if angle_open { | ||
1078 | write!(f, ">")?; | ||
1079 | } | ||
1080 | } | 1061 | } |
1081 | Ty::Unknown => write!(f, "{{unknown}}")?, | 1062 | Ty::Unknown => write!(f, "{{unknown}}")?, |
1082 | Ty::Infer(..) => write!(f, "_")?, | 1063 | Ty::Infer(..) => write!(f, "_")?, |
@@ -1085,6 +1066,71 @@ impl HirDisplay for Ty { | |||
1085 | } | 1066 | } |
1086 | } | 1067 | } |
1087 | 1068 | ||
1069 | fn write_bounds_like_dyn_trait( | ||
1070 | predicates: &[GenericPredicate], | ||
1071 | f: &mut HirFormatter<impl HirDatabase>, | ||
1072 | ) -> fmt::Result { | ||
1073 | // Note: This code is written to produce nice results (i.e. | ||
1074 | // corresponding to surface Rust) for types that can occur in | ||
1075 | // actual Rust. It will have weird results if the predicates | ||
1076 | // aren't as expected (i.e. self types = $0, projection | ||
1077 | // predicates for a certain trait come after the Implemented | ||
1078 | // predicate for that trait). | ||
1079 | let mut first = true; | ||
1080 | let mut angle_open = false; | ||
1081 | for p in predicates.iter() { | ||
1082 | match p { | ||
1083 | GenericPredicate::Implemented(trait_ref) => { | ||
1084 | if angle_open { | ||
1085 | write!(f, ">")?; | ||
1086 | } | ||
1087 | if !first { | ||
1088 | write!(f, " + ")?; | ||
1089 | } | ||
1090 | // We assume that the self type is $0 (i.e. the | ||
1091 | // existential) here, which is the only thing that's | ||
1092 | // possible in actual Rust, and hence don't print it | ||
1093 | write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; | ||
1094 | if trait_ref.substs.len() > 1 { | ||
1095 | write!(f, "<")?; | ||
1096 | f.write_joined(&trait_ref.substs[1..], ", ")?; | ||
1097 | // there might be assoc type bindings, so we leave the angle brackets open | ||
1098 | angle_open = true; | ||
1099 | } | ||
1100 | } | ||
1101 | GenericPredicate::Projection(projection_pred) => { | ||
1102 | // in types in actual Rust, these will always come | ||
1103 | // after the corresponding Implemented predicate | ||
1104 | if angle_open { | ||
1105 | write!(f, ", ")?; | ||
1106 | } else { | ||
1107 | write!(f, "<")?; | ||
1108 | angle_open = true; | ||
1109 | } | ||
1110 | let name = | ||
1111 | f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone(); | ||
1112 | write!(f, "{} = ", name)?; | ||
1113 | projection_pred.ty.hir_fmt(f)?; | ||
1114 | } | ||
1115 | GenericPredicate::Error => { | ||
1116 | if angle_open { | ||
1117 | // impl Trait<X, {error}> | ||
1118 | write!(f, ", ")?; | ||
1119 | } else if !first { | ||
1120 | // impl Trait + {error} | ||
1121 | write!(f, " + ")?; | ||
1122 | } | ||
1123 | p.hir_fmt(f)?; | ||
1124 | } | ||
1125 | } | ||
1126 | first = false; | ||
1127 | } | ||
1128 | if angle_open { | ||
1129 | write!(f, ">")?; | ||
1130 | } | ||
1131 | Ok(()) | ||
1132 | } | ||
1133 | |||
1088 | impl TraitRef { | 1134 | impl TraitRef { |
1089 | fn hir_fmt_ext(&self, f: &mut HirFormatter<impl HirDatabase>, use_as: bool) -> fmt::Result { | 1135 | fn hir_fmt_ext(&self, f: &mut HirFormatter<impl HirDatabase>, use_as: bool) -> fmt::Result { |
1090 | if f.should_truncate() { | 1136 | if f.should_truncate() { |