From 421979bc686216937b54b5895210576fe5f60dbb Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Tue, 15 Jun 2021 13:53:20 +0500 Subject: [PATCH] HirDisplay prints `?Sized` bounds now; `impl Trait: Sized` by default. --- crates/hir/src/display.rs | 4 +- crates/hir_ty/src/display.rs | 58 ++++++++++++++- crates/hir_ty/src/lower.rs | 43 +++++++++-- crates/hir_ty/src/tests/coercion.rs | 2 +- .../hir_ty/src/tests/display_source_code.rs | 74 +++++++++++++++++++ 5 files changed, 167 insertions(+), 14 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 98c1f1c227c9..df9094ac82de 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -7,7 +7,7 @@ use hir_def::{ }; use hir_ty::display::{ write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, - HirFormatter, + HirFormatter, SizedByDefault, }; use hir_ty::Interner; use syntax::ast::{self, NameOwner}; @@ -239,7 +239,7 @@ impl HirDisplay for TypeParam { let predicates = bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect::>(); if !(predicates.is_empty() || f.omit_verbose_types()) { - write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?; + write_bounds_like_dyn_trait_with_prefix(":", &predicates, SizedByDefault::Sized, f)?; } Ok(()) } diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index d074c19a3e4c..df7cf1c39c41 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -582,7 +582,12 @@ impl HirDisplay for Ty { .as_ref() .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); let bounds = data.substitute(&Interner, ¶meters); - write_bounds_like_dyn_trait_with_prefix("impl", bounds.skip_binders(), f)?; + write_bounds_like_dyn_trait_with_prefix( + "impl", + bounds.skip_binders(), + SizedByDefault::Sized, + f, + )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } ImplTraitId::AsyncBlockTypeImplTrait(..) => { @@ -641,7 +646,12 @@ impl HirDisplay for Ty { _ => false, }) .collect::>(); - write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?; + write_bounds_like_dyn_trait_with_prefix( + "impl", + &bounds, + SizedByDefault::Sized, + f, + )?; } } } @@ -650,6 +660,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( "dyn", dyn_ty.bounds.skip_binders().interned(), + SizedByDefault::NotSized, f, )?; } @@ -664,7 +675,12 @@ impl HirDisplay for Ty { .as_ref() .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); let bounds = data.substitute(&Interner, &opaque_ty.substitution); - write_bounds_like_dyn_trait_with_prefix("impl", bounds.skip_binders(), f)?; + write_bounds_like_dyn_trait_with_prefix( + "impl", + bounds.skip_binders(), + SizedByDefault::Sized, + f, + )?; } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; @@ -713,15 +729,29 @@ fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator Option { + let krate = trait_.lookup(db).container.krate(); + let sized_trait = + db.lang_item(krate, "sized".into()).and_then(|lang_item| lang_item.as_trait())?; + Some(trait_ == sized_trait) +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum SizedByDefault { + NotSized, + Sized, +} + pub fn write_bounds_like_dyn_trait_with_prefix( prefix: &str, predicates: &[QuantifiedWhereClause], + default_sized: SizedByDefault, f: &mut HirFormatter, ) -> Result<(), HirDisplayError> { write!(f, "{}", prefix)?; if !predicates.is_empty() { write!(f, " ")?; - write_bounds_like_dyn_trait(predicates, f) + write_bounds_like_dyn_trait(predicates, default_sized, f) } else { Ok(()) } @@ -729,6 +759,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix( fn write_bounds_like_dyn_trait( predicates: &[QuantifiedWhereClause], + default_sized: SizedByDefault, f: &mut HirFormatter, ) -> Result<(), HirDisplayError> { // Note: This code is written to produce nice results (i.e. @@ -740,10 +771,22 @@ fn write_bounds_like_dyn_trait( let mut first = true; let mut angle_open = false; let mut is_fn_trait = false; + let mut is_sized = None; for p in predicates.iter() { match p.skip_binders() { WhereClause::Implemented(trait_ref) => { let trait_ = trait_ref.hir_trait_id(); + match is_sized_trait(f.db.upcast(), trait_) { + Some(true) => { + is_sized = Some(true); + if default_sized == SizedByDefault::Sized { + // Don't print +Sized, but rather +?Sized if absent. + continue; + } + } + Some(false) => is_sized = is_sized.or(Some(false)), + None => (), + } if !is_fn_trait { is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_); } @@ -808,6 +851,13 @@ fn write_bounds_like_dyn_trait( if angle_open { write!(f, ">")?; } + if default_sized == SizedByDefault::Sized && is_sized.is_some() { + if is_sized == Some(false) { + write!(f, "{}?Sized", if first { "" } else { " + " })?; + } else if first { + write!(f, "Sized")?; + } + } Ok(()) } diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 4544c6138935..2bcd0fcb5db7 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -226,6 +226,10 @@ impl<'a> TyLoweringContext<'a> { ImplTraitLoweringMode::Opaque => { let idx = self.impl_trait_counter.get(); self.impl_trait_counter.set(idx + 1); + let func = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(f)) => f, + _ => panic!("opaque impl trait lowering in non-function"), + }; assert!(idx as usize == self.opaque_type_data.borrow().len()); // this dance is to make sure the data is in the right @@ -245,14 +249,10 @@ impl<'a> TyLoweringContext<'a> { // away instead of two. let actual_opaque_type_data = self .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { - ctx.lower_impl_trait(bounds) + ctx.lower_impl_trait(bounds, func) }); self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; - let func = match self.resolver.generic_def() { - Some(GenericDefId::FunctionId(f)) => f, - _ => panic!("opaque impl trait lowering in non-function"), - }; let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); let generics = generics(self.db.upcast(), func.into()); @@ -871,13 +871,42 @@ impl<'a> TyLoweringContext<'a> { }) } - fn lower_impl_trait(&self, bounds: &[Interned]) -> ReturnTypeImplTrait { + fn lower_impl_trait( + &self, + bounds: &[Interned], + func: FunctionId, + ) -> ReturnTypeImplTrait { cov_mark::hit!(lower_rpit); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner); + // XXX(iDawer): Can shifting mess with unsized_types? For now I better reinsure. + let outer_unsized_types = self.unsized_types.replace(Default::default()); let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)).collect() + let mut predicates: Vec<_> = bounds + .iter() + .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)) + .collect(); + + if !ctx.unsized_types.borrow().contains(&self_ty) { + let krate = func.lookup(ctx.db.upcast()).module(ctx.db.upcast()).krate(); + let sized_trait = ctx + .db + .lang_item(krate, "sized".into()) + .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); + let sized_clause = sized_trait.map(|trait_id| { + let clause = WhereClause::Implemented(TraitRef { + trait_id, + substitution: Substitution::from1(&Interner, self_ty.clone()), + }); + crate::wrap_empty_binders(clause) + }); + predicates.extend(sized_clause.into_iter()); + predicates.shrink_to_fit(); + } + predicates }); + self.unsized_types.replace(outer_unsized_types); + ReturnTypeImplTrait { bounds: crate::make_only_type_binders(1, predicates) } } } diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index abf00d91ff06..cef1ffe9c9e4 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -406,7 +406,7 @@ trait Foo {} fn test(f: impl Foo, g: &(impl Foo + ?Sized)) { let _: &dyn Foo = &f; let _: &dyn Foo = g; - //^ expected &dyn Foo, got &impl Foo + //^ expected &dyn Foo, got &impl Foo + ?Sized } "#, ); diff --git a/crates/hir_ty/src/tests/display_source_code.rs b/crates/hir_ty/src/tests/display_source_code.rs index a484dc3a03b8..cc2b779072ad 100644 --- a/crates/hir_ty/src/tests/display_source_code.rs +++ b/crates/hir_ty/src/tests/display_source_code.rs @@ -67,3 +67,77 @@ fn foo(foo: &dyn for<'a> Foo<'a>) {} "#, ); } + +#[test] +fn sized_bounds_apit() { + check_types_source_code( + r#" +#[lang = "sized"] +pub trait Sized {} + +trait Foo {} +trait Bar {} +struct S; +fn test( + a: impl Foo, + b: impl Foo + Sized, + c: &(impl Foo + ?Sized), + d: S, + e: impl Bar, + empty: impl, +) { + a; + //^ impl Foo + b; + //^ impl Foo + c; + //^ &impl Foo + ?Sized + d; + //^ S + e; + //^ impl Bar + empty; +} //^ impl Sized +"#, + ); +} + +#[test] +fn sized_bounds_rpit() { + check_types_source_code( + r#" +#[lang = "sized"] +pub trait Sized {} + +trait Foo {} +fn foo() -> impl Foo { loop {} } +fn test() { + let foo = foo(); + foo; +} //^ impl Foo +"#, + ); +} + +#[test] +fn sized_bounds_impl_traits_in_fn_signature() { + check_types_source_code( + r#" +#[lang = "sized"] +pub trait Sized {} + +trait Foo {} +fn test( + a: fn(impl Foo) -> impl Foo, + b: fn(impl Foo + Sized) -> impl Foo + Sized, + c: fn(&(impl Foo + ?Sized)) -> &(impl Foo + ?Sized), +) { + a; + //^ fn(impl Foo) -> impl Foo + b; + //^ fn(impl Foo) -> impl Foo + c; +} //^ fn(&impl Foo + ?Sized) -> &impl Foo + ?Sized +"#, + ); +}