HirDisplay prints ?Sized bounds now; impl Trait: Sized by default.

This commit is contained in:
Dawer 2021-06-15 13:53:20 +05:00
parent d9e6377b91
commit 421979bc68
5 changed files with 167 additions and 14 deletions

View File

@ -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::<Vec<_>>();
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(())
}

View File

@ -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, &parameters);
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::<Vec<_>>();
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<Item = Trai
utils::fn_traits(db, krate)
}
fn is_sized_trait(db: &dyn DefDatabase, trait_: TraitId) -> Option<bool> {
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(())
}

View File

@ -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<TypeBound>]) -> ReturnTypeImplTrait {
fn lower_impl_trait(
&self,
bounds: &[Interned<TypeBound>],
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) }
}
}

View File

@ -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
}
"#,
);

View File

@ -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<T> {}
struct S<T>;
fn test(
a: impl Foo,
b: impl Foo + Sized,
c: &(impl Foo + ?Sized),
d: S<impl Foo>,
e: impl Bar<impl Foo>,
empty: impl,
) {
a;
//^ impl Foo
b;
//^ impl Foo
c;
//^ &impl Foo + ?Sized
d;
//^ S<impl Foo>
e;
//^ impl Bar<impl Foo>
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<T: Foo>() {
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
"#,
);
}