diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-08-12 17:55:29 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-12 17:55:29 +0000 |
| commit | baf149437452f6aec9f873bb312723bf62cb3a74 (patch) | |
| tree | aa9f703c7982944aed7d7393bb0db0f653686b2d | |
| parent | c619cb12d7f7c1cf65d44007b144b4c9bcafc7c5 (diff) | |
| parent | 6c366ade0087db292b9a1eee71b6a20653a4d0e9 (diff) | |
Merge #9807
9807: Implicit `Sized` bounds r=iDawer a=iDawer This should close #8984 `hir_ty`: - Type parameters, associated types and `impl Trait` are `Sized` by deafault except `Self` in a trait. - Implicit `Sized` bound is added to end of predicate list. It does not check if such bound is present already. Also it does not track the bound is implicit. - Allowed ambiguous unsize coercion if Chalk returns definite guidance. - Allowed ambiguous autoderef if Chalk returns definite guidance. `hir_def`: - `ItemTree` pretty printing shows `?Sized` bounds. `HirDisplay`: - `impl Trait` with weird bounds rendered correctly. - `Sized`/`?Sized` bounds are not shown if they are default. ### Perf `./target/rust-analyzer-baseline_8a843113 -q analysis-stats --memory-usage .` ``` Database loaded: 1.63s, 287minstr, 91mb crates: 38, mods: 741, decls: 15914, fns: 11835 Item Collection: 26.80s, 73ginstr, 338mb exprs: 318994, ??ty: 398 (0%), ?ty: 435 (0%), !ty: 174 Inference: 50.28s, 116ginstr, 516mb Total: 77.08s, 189ginstr, 855mb ``` `./target/rust-analyzer-sized-fixed_ambig_coercion-de074fe6 -q analysis-stats --memory-usage .` ``` Database loaded: 1.63s, 287minstr, 91mb crates: 38, mods: 741, decls: 15914, fns: 11835 Item Collection: 26.95s, 73ginstr, 338mb exprs: 318994, ??ty: 398 (0%), ?ty: 435 (0%), !ty: 166 Inference: 96.39s, 234ginstr, 543mb Total: 123.33s, 307ginstr, 881mb ``` Co-authored-by: Dawer <7803845+iDawer@users.noreply.github.com>
| -rw-r--r-- | crates/hir/src/display.rs | 7 | ||||
| -rw-r--r-- | crates/hir_def/src/generics.rs | 4 | ||||
| -rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 4 | ||||
| -rw-r--r-- | crates/hir_def/src/item_tree/pretty.rs | 9 | ||||
| -rw-r--r-- | crates/hir_def/src/item_tree/tests.rs | 8 | ||||
| -rw-r--r-- | crates/hir_def/src/type_ref.rs | 27 | ||||
| -rw-r--r-- | crates/hir_ty/src/autoderef.rs | 19 | ||||
| -rw-r--r-- | crates/hir_ty/src/chalk_db.rs | 17 | ||||
| -rw-r--r-- | crates/hir_ty/src/display.rs | 145 | ||||
| -rw-r--r-- | crates/hir_ty/src/infer/coerce.rs | 12 | ||||
| -rw-r--r-- | crates/hir_ty/src/lower.rs | 130 | ||||
| -rw-r--r-- | crates/hir_ty/src/tests/coercion.rs | 19 | ||||
| -rw-r--r-- | crates/hir_ty/src/tests/display_source_code.rs | 111 | ||||
| -rw-r--r-- | crates/hir_ty/src/tests/method_resolution.rs | 27 | ||||
| -rw-r--r-- | crates/hir_ty/src/tests/regression.rs | 3 | ||||
| -rw-r--r-- | crates/hir_ty/src/tests/traits.rs | 91 | ||||
| -rw-r--r-- | crates/hir_ty/src/utils.rs | 6 | ||||
| -rw-r--r-- | crates/ide/src/hover.rs | 28 | ||||
| -rw-r--r-- | crates/ide/src/inlay_hints.rs | 48 | ||||
| -rw-r--r-- | crates/ide_assists/src/handlers/generate_function.rs | 9 |
20 files changed, 620 insertions, 104 deletions
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 72f0d9b5f79..dff4372738b 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}; @@ -93,7 +93,7 @@ impl HirDisplay for Function { } else { match &*data.ret_type { TypeRef::ImplTrait(bounds) => match bounds[0].as_ref() { - TypeBound::Path(path) => { + TypeBound::Path(path, _) => { path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings [0] .type_ref @@ -239,7 +239,8 @@ 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)?; + let default_sized = SizedByDefault::Sized { anchor: self.module(f.db).krate().id }; + write_bounds_like_dyn_trait_with_prefix(":", &predicates, default_sized, f)?; } Ok(()) } diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs index 096ac796850..40104833db3 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs @@ -338,10 +338,6 @@ impl GenericParams { hrtb_lifetimes: Option<&Box<[Name]>>, target: Either<TypeRef, LifetimeRef>, ) { - if bound.question_mark_token().is_some() { - // FIXME: remove this bound - return; - } let bound = TypeBound::from_ast(lower_ctx, bound); let predicate = match (target, bound) { (Either::Left(type_ref), bound) => match hrtb_lifetimes { diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 44bd2fa9e57..2f9e33b6adc 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -10,7 +10,7 @@ use syntax::{ use crate::{ generics::{GenericParams, TypeParamData, TypeParamProvenance}, - type_ref::{LifetimeRef, TraitRef}, + type_ref::{LifetimeRef, TraitBoundModifier, TraitRef}, }; use super::*; @@ -369,7 +369,7 @@ impl<'a> Ctx<'a> { let (ret_type, async_ret_type) = if func.async_token().is_some() { let async_ret_type = ret_type.clone(); let future_impl = desugar_future_path(ret_type); - let ty_bound = Interned::new(TypeBound::Path(future_impl)); + let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None)); (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type)) } else { (ret_type, None) diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs index 603c594f2aa..24cc2408de2 100644 --- a/crates/hir_def/src/item_tree/pretty.rs +++ b/crates/hir_def/src/item_tree/pretty.rs @@ -8,6 +8,7 @@ use crate::{ attr::RawAttrs, generics::{WherePredicate, WherePredicateTypeTarget}, path::GenericArg, + type_ref::TraitBoundModifier, visibility::RawVisibility, }; @@ -543,7 +544,13 @@ impl<'a> Printer<'a> { } match bound.as_ref() { - TypeBound::Path(path) => self.print_path(path), + TypeBound::Path(path, modifier) => { + match modifier { + TraitBoundModifier::None => (), + TraitBoundModifier::Maybe => w!(self, "?"), + } + self.print_path(path) + } TypeBound::ForLifetime(lifetimes, path) => { w!(self, "for<{}> ", lifetimes.iter().format(", ")); self.print_path(path); diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs index cbaa6d086db..a153e17b17a 100644 --- a/crates/hir_def/src/item_tree/tests.rs +++ b/crates/hir_def/src/item_tree/tests.rs @@ -304,7 +304,7 @@ struct S<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> { field: &'a &'b T, } -struct Tuple<T: Copy>(T); +struct Tuple<T: Copy, U: ?Sized>(T, U); impl<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> S<'a, 'b, T, K> { fn f<G: 'a>(arg: impl Copy) -> impl Copy {} @@ -325,11 +325,13 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} pub(self) field: &'a &'b T, } - pub(self) struct Tuple<T>( + pub(self) struct Tuple<T, U>( pub(self) 0: T, + pub(self) 1: U, ) where - T: Copy; + T: Copy, + U: ?Sized; impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K> where diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index 1d35f02ca1d..9f33fd26e84 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs @@ -118,12 +118,20 @@ impl LifetimeRef { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum TypeBound { - Path(Path), + Path(Path, TraitBoundModifier), ForLifetime(Box<[Name]>, Path), Lifetime(LifetimeRef), Error, } +/// A modifier on a bound, currently this is only used for `?Sized`, where the +/// modifier is `Maybe`. +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum TraitBoundModifier { + None, + Maybe, +} + impl TypeRef { /// Converts an `ast::TypeRef` to a `hir::TypeRef`. pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { @@ -233,7 +241,7 @@ impl TypeRef { TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { for bound in bounds { match bound.as_ref() { - TypeBound::Path(path) | TypeBound::ForLifetime(_, path) => { + TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { go_path(path, f) } TypeBound::Lifetime(_) | TypeBound::Error => (), @@ -265,7 +273,7 @@ impl TypeRef { } for bound in &binding.bounds { match bound.as_ref() { - TypeBound::Path(path) | TypeBound::ForLifetime(_, path) => { + TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { go_path(path, f) } TypeBound::Lifetime(_) | TypeBound::Error => (), @@ -295,7 +303,13 @@ impl TypeBound { match node.kind() { ast::TypeBoundKind::PathType(path_type) => { - lower_path_type(path_type).map(TypeBound::Path).unwrap_or(TypeBound::Error) + let m = match node.question_mark_token() { + Some(_) => TraitBoundModifier::Maybe, + None => TraitBoundModifier::None, + }; + lower_path_type(path_type) + .map(|p| TypeBound::Path(p, m)) + .unwrap_or(TypeBound::Error) } ast::TypeBoundKind::ForType(for_type) => { let lt_refs = match for_type.generic_param_list() { @@ -320,9 +334,10 @@ impl TypeBound { } } - pub fn as_path(&self) -> Option<&Path> { + pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> { match self { - TypeBound::Path(p) | TypeBound::ForLifetime(_, p) => Some(p), + TypeBound::Path(p, m) => Some((p, m)), + TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)), TypeBound::Lifetime(_) | TypeBound::Error => None, } } diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index 267d08d69a6..0be337fc2df 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs @@ -14,8 +14,8 @@ use log::{info, warn}; use crate::{ db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, - DebruijnIndex, Environment, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, - Ty, TyBuilder, TyKind, + ConstrainedSubst, DebruijnIndex, Environment, Guidance, InEnvironment, Interner, + ProjectionTyExt, Solution, Substitution, Ty, TyBuilder, TyKind, }; const AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10); @@ -187,7 +187,8 @@ fn deref_by_trait( let solution = db.trait_solve(krate, canonical)?; match &solution { - Solution::Unique(vars) => { + Solution::Unique(Canonical { value: ConstrainedSubst { subst, .. }, binders }) + | Solution::Ambig(Guidance::Definite(Canonical { value: subst, binders })) => { // FIXME: vars may contain solutions for any inference variables // that happened to be inside ty. To correctly handle these, we // would have to pass the solution up to the inference context, but @@ -203,8 +204,8 @@ fn deref_by_trait( // assumptions will be broken. We would need to properly introduce // new variables in that case - for i in 1..vars.binders.len(&Interner) { - if vars.value.subst.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner) + for i in 1..binders.len(&Interner) { + if subst.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner) != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) { warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution); @@ -214,13 +215,11 @@ fn deref_by_trait( // FIXME: we remove lifetime variables here since they can confuse // the method resolution code later Some(fixup_lifetime_variables(Canonical { - value: vars - .value - .subst - .at(&Interner, vars.value.subst.len(&Interner) - 1) + value: subst + .at(&Interner, subst.len(&Interner) - 1) .assert_ty_ref(&Interner) .clone(), - binders: vars.binders.clone(), + binders: binders.clone(), })) } Solution::Ambig(_) => { diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs index a55b99de042..333d934ed37 100644 --- a/crates/hir_ty/src/chalk_db.rs +++ b/crates/hir_ty/src/chalk_db.rs @@ -412,13 +412,28 @@ pub(crate) fn associated_ty_data_query( .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); let self_ty = TyKind::BoundVar(BoundVar::new(crate::DebruijnIndex::INNERMOST, 0)).intern(&Interner); - let bounds = type_alias_data + let mut bounds: Vec<_> = type_alias_data .bounds .iter() .flat_map(|bound| ctx.lower_type_bound(bound, self_ty.clone(), false)) .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty)) .collect(); + if !ctx.unsized_types.borrow().contains(&self_ty) { + let sized_trait = resolver + .krate() + .and_then(|krate| db.lang_item(krate, "sized".into())) + .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); + let sized_bound = sized_trait.into_iter().map(|sized_trait| { + let trait_bound = + rust_ir::TraitBound { trait_id: sized_trait, args_no_self: Default::default() }; + let inline_bound = rust_ir::InlineBound::TraitBound(trait_bound); + chalk_ir::Binders::empty(&Interner, inline_bound) + }); + bounds.extend(sized_bound); + bounds.shrink_to_fit(); + } + // FIXME: Re-enable where clauses on associated types when an upstream chalk bug is fixed. // (rust-analyzer#9052) // let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 3f7455959d7..91c705afb78 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -4,6 +4,7 @@ use std::fmt::{self, Debug}; +use base_db::CrateId; use chalk_ir::BoundVar; use hir_def::{ body, @@ -13,9 +14,9 @@ use hir_def::{ intern::{Internable, Interned}, item_scope::ItemInNs, path::{Path, PathKind}, - type_ref::{TypeBound, TypeRef}, + type_ref::{TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - AssocContainerId, Lookup, ModuleId, TraitId, + AssocContainerId, HasModule, Lookup, ModuleId, TraitId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use itertools::Itertools; @@ -376,10 +377,20 @@ impl HirDisplay for Ty { } // FIXME: all this just to decide whether to use parentheses... - let datas; - let predicates: Vec<_> = match t.kind(&Interner) { + let contains_impl_fn = |bounds: &[QuantifiedWhereClause]| { + bounds.iter().any(|bound| { + if let WhereClause::Implemented(trait_ref) = bound.skip_binders() { + let trait_ = trait_ref.hir_trait_id(); + fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) + } else { + false + } + }) + }; + let (preds_to_print, has_impl_fn_pred) = match t.kind(&Interner) { TyKind::Dyn(dyn_ty) if dyn_ty.bounds.skip_binders().interned().len() > 1 => { - dyn_ty.bounds.skip_binders().interned().iter().cloned().collect() + let bounds = dyn_ty.bounds.skip_binders().interned(); + (bounds.len(), contains_impl_fn(bounds)) } TyKind::Alias(AliasTy::Opaque(OpaqueTy { opaque_ty_id, @@ -389,33 +400,54 @@ impl HirDisplay for Ty { let impl_trait_id = f.db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id { - datas = + let datas = f.db.return_type_impl_traits(func) .expect("impl trait id without data"); let data = (*datas) .as_ref() .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); let bounds = data.substitute(&Interner, parameters); - bounds.into_value_and_skipped_binders().0 + let mut len = bounds.skip_binders().len(); + + // Don't count Sized but count when it absent + // (i.e. when explicit ?Sized bound is set). + let default_sized = SizedByDefault::Sized { + anchor: func.lookup(f.db.upcast()).module(f.db.upcast()).krate(), + }; + let sized_bounds = bounds + .skip_binders() + .iter() + .filter(|b| { + matches!( + b.skip_binders(), + WhereClause::Implemented(trait_ref) + if default_sized.is_sized_trait( + trait_ref.hir_trait_id(), + f.db.upcast(), + ), + ) + }) + .count(); + match sized_bounds { + 0 => len += 1, + _ => { + len = len.saturating_sub(sized_bounds); + } + } + + (len, contains_impl_fn(bounds.skip_binders())) } else { - Vec::new() + (0, false) } } - _ => Vec::new(), + _ => (0, false), }; - if let Some(WhereClause::Implemented(trait_ref)) = - predicates.get(0).map(|b| b.skip_binders()) - { - let trait_ = trait_ref.hir_trait_id(); - if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) - && predicates.len() <= 2 - { - return t.hir_fmt(f); - } + if has_impl_fn_pred && preds_to_print <= 2 { + return t.hir_fmt(f); } - if predicates.len() > 1 { + if preds_to_print > 1 { write!(f, "(")?; t.hir_fmt(f)?; write!(f, ")")?; @@ -582,7 +614,13 @@ 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)?; + let krate = func.lookup(f.db.upcast()).module(f.db.upcast()).krate(); + write_bounds_like_dyn_trait_with_prefix( + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + 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 +679,13 @@ impl HirDisplay for Ty { _ => false, }) .collect::<Vec<_>>(); - write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?; + let krate = id.parent.module(f.db.upcast()).krate(); + write_bounds_like_dyn_trait_with_prefix( + "impl", + &bounds, + SizedByDefault::Sized { anchor: krate }, + f, + )?; } } } @@ -650,6 +694,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( "dyn", dyn_ty.bounds.skip_binders().interned(), + SizedByDefault::NotSized, f, )?; } @@ -664,7 +709,13 @@ 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)?; + let krate = func.lookup(f.db.upcast()).module(f.db.upcast()).krate(); + write_bounds_like_dyn_trait_with_prefix( + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + f, + )?; } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; @@ -713,15 +764,37 @@ fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = Trai utils::fn_traits(db, krate) } +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum SizedByDefault { + NotSized, + Sized { anchor: CrateId }, +} + +impl SizedByDefault { + fn is_sized_trait(self, trait_: TraitId, db: &dyn DefDatabase) -> bool { + match self { + Self::NotSized => false, + Self::Sized { anchor } => { + let sized_trait = + db.lang_item(anchor, "sized".into()).and_then(|lang_item| lang_item.as_trait()); + Some(trait_) == sized_trait + } + } + } +} + 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() { + if !predicates.is_empty() + || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. }) + { write!(f, " ")?; - write_bounds_like_dyn_trait(predicates, f) + write_bounds_like_dyn_trait(predicates, default_sized, f) } else { Ok(()) } @@ -729,6 +802,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 +814,18 @@ 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 = false; for p in predicates.iter() { match p.skip_binders() { WhereClause::Implemented(trait_ref) => { let trait_ = trait_ref.hir_trait_id(); + if default_sized.is_sized_trait(trait_, f.db.upcast()) { + is_sized = true; + if matches!(default_sized, SizedByDefault::Sized { .. }) { + // Don't print +Sized, but rather +?Sized if absent. + continue; + } + } if !is_fn_trait { is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_); } @@ -808,6 +890,13 @@ fn write_bounds_like_dyn_trait( if angle_open { write!(f, ">")?; } + if matches!(default_sized, SizedByDefault::Sized { .. }) { + if !is_sized { + write!(f, "{}?Sized", if first { "" } else { " + " })?; + } else if first { + write!(f, "Sized")?; + } + } Ok(()) } @@ -1026,7 +1115,13 @@ impl HirDisplay for TypeRef { impl HirDisplay for TypeBound { fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { match self { - TypeBound::Path(path) => path.hir_fmt(f), + TypeBound::Path(path, modifier) => { + match modifier { + TraitBoundModifier::None => (), + TraitBoundModifier::Maybe => write!(f, "?")?, + } + path.hir_fmt(f) + } TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name), TypeBound::ForLifetime(lifetimes, path) => { write!(f, "for<{}> ", lifetimes.iter().format(", "))?; diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index d2e999a730f..0f5859c433f 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -17,8 +17,8 @@ use crate::{ Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, }, - static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, InEnvironment, Interner, Solution, - Substitution, Ty, TyBuilder, TyExt, TyKind, + static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, + Solution, Substitution, Ty, TyBuilder, TyExt, TyKind, }; pub(crate) type CoerceResult = Result<InferOk<(Vec<Adjustment>, Ty)>, TypeError>; @@ -541,7 +541,7 @@ impl<'a> InferenceContext<'a> { _ => return Err(TypeError), }; - let trait_ref = { + let coerce_unsized_tref = { let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); if b.remaining() != 2 { // The CoerceUnsized trait should have two generic params: Self and T. @@ -551,7 +551,7 @@ impl<'a> InferenceContext<'a> { }; let goal: InEnvironment<DomainGoal> = - InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner)); + InEnvironment::new(&self.trait_env.env, coerce_unsized_tref.cast(&Interner)); let canonicalized = self.canonicalize(goal); @@ -575,7 +575,9 @@ impl<'a> InferenceContext<'a> { }, ); } - // FIXME: should we accept ambiguous results here? + Solution::Ambig(Guidance::Definite(subst)) => { + canonicalized.apply_solution(&mut self.table, subst) + } _ => return Err(TypeError), }; let unsize = diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 5ba66fda8f8..8cac285cb19 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -18,13 +18,14 @@ use hir_def::{ generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, path::{GenericArg, Path, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, - type_ref::{TraitRef as HirTraitRef, TypeBound, TypeRef}, + type_ref::{TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef}, AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, }; use hir_expand::{name::Name, ExpandResult}; use la_arena::ArenaMap; +use rustc_hash::FxHashSet; use smallvec::SmallVec; use stdx::impl_from; use syntax::ast; @@ -65,6 +66,8 @@ pub struct TyLoweringContext<'a> { /// Splitting this up would be a possible fix. opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>, expander: RefCell<Option<Expander>>, + /// Tracks types with explicit `?Sized` bounds. + pub(crate) unsized_types: RefCell<FxHashSet<Ty>>, } impl<'a> TyLoweringContext<'a> { @@ -83,6 +86,7 @@ impl<'a> TyLoweringContext<'a> { type_param_mode, opaque_type_data, expander: RefCell::new(None), + unsized_types: RefCell::default(), } } @@ -93,17 +97,20 @@ impl<'a> TyLoweringContext<'a> { ) -> T { let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); let expander = self.expander.replace(None); + let unsized_types = self.unsized_types.replace(Default::default()); let new_ctx = Self { in_binders: debruijn, impl_trait_counter: Cell::new(self.impl_trait_counter.get()), opaque_type_data: RefCell::new(opaque_ty_data_vec), expander: RefCell::new(expander), + unsized_types: RefCell::new(unsized_types), ..*self }; let result = f(&new_ctx); self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); self.expander.replace(new_ctx.expander.into_inner()); + self.unsized_types.replace(new_ctx.unsized_types.into_inner()); result } @@ -219,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 @@ -238,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()); @@ -776,10 +783,27 @@ impl<'a> TyLoweringContext<'a> { ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a { let mut bindings = None; let trait_ref = match bound { - TypeBound::Path(path) => { + TypeBound::Path(path, TraitBoundModifier::None) => { bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } + TypeBound::Path(path, TraitBoundModifier::Maybe) => { + let sized_trait = self + .resolver + .krate() + .and_then(|krate| self.db.lang_item(krate, "sized".into())) + .and_then(|lang_item| lang_item.as_trait()); + // Don't lower associated type bindings as the only possible relaxed trait bound + // `?Sized` has no of them. + // If we got another trait here ignore the bound completely. + let trait_id = self + .lower_trait_ref_from_path(path, Some(self_ty.clone())) + .map(|trait_ref| trait_ref.hir_trait_id()); + if trait_id == sized_trait { + self.unsized_types.borrow_mut().insert(self_ty); + } + None + } TypeBound::ForLifetime(_, path) => { // FIXME Don't silently drop the hrtb lifetimes here bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); @@ -802,8 +826,12 @@ impl<'a> TyLoweringContext<'a> { trait_ref: TraitRef, ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a { let last_segment = match bound { - TypeBound::Path(path) | TypeBound::ForLifetime(_, path) => path.segments().last(), - TypeBound::Error | TypeBound::Lifetime(_) => None, + TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => { + path.segments().last() + } + TypeBound::Path(_, TraitBoundModifier::Maybe) + | TypeBound::Error + | TypeBound::Lifetime(_) => None, }; last_segment .into_iter() @@ -843,13 +871,39 @@ 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); 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 }); + ReturnTypeImplTrait { bounds: crate::make_only_type_binders(1, predicates) } } } @@ -1008,7 +1062,7 @@ pub(crate) fn trait_environment_query( traits_in_scope .push((tr.self_type_parameter(&Interner).clone(), tr.hir_trait_id())); } - let program_clause: chalk_ir::ProgramClause<Interner> = pred.clone().cast(&Interner); + let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(&Interner); clauses.push(program_clause.into_from_env_clause(&Interner)); } } @@ -1035,6 +1089,15 @@ pub(crate) fn trait_environment_query( clauses.push(program_clause.into_from_env_clause(&Interner)); } + let subst = generics(db.upcast(), def).type_params_subst(db); + let explicitly_unsized_tys = ctx.unsized_types.into_inner(); + let implicitly_sized_clauses = + implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver).map(|pred| { + let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(&Interner); + program_clause.into_from_env_clause(&Interner) + }); + clauses.extend(implicitly_sized_clauses); + let krate = def.module(db.upcast()).krate(); let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses); @@ -1051,10 +1114,49 @@ pub(crate) fn generic_predicates_query( let ctx = TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); let generics = generics(db.upcast(), def); - resolver + + let mut predicates = resolver .where_predicates_in_scope() .flat_map(|pred| ctx.lower_where_predicate(pred, false).map(|p| make_binders(&generics, p))) - .collect() + .collect::<Vec<_>>(); + + let subst = generics.bound_vars_subst(DebruijnIndex::INNERMOST); + let explicitly_unsized_tys = ctx.unsized_types.into_inner(); + let implicitly_sized_predicates = + implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) + .map(|p| make_binders(&generics, crate::wrap_empty_binders(p))); + predicates.extend(implicitly_sized_predicates); + predicates.into() +} + +/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. +/// Exception is Self of a trait def. +fn implicitly_sized_clauses<'a>( + db: &dyn HirDatabase, + def: GenericDefId, + explicitly_unsized_tys: &'a FxHashSet<Ty>, + substitution: &'a Substitution, + resolver: &Resolver, +) -> impl Iterator<Item = WhereClause> + 'a { + let is_trait_def = matches!(def, GenericDefId::TraitId(..)); + let generic_args = &substitution.as_slice(&Interner)[is_trait_def as usize..]; + let sized_trait = resolver + .krate() + .and_then(|krate| db.lang_item(krate, "sized".into())) + .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); + + sized_trait.into_iter().flat_map(move |sized_trait| { + let implicitly_sized_tys = generic_args + .iter() + .filter_map(|generic_arg| generic_arg.ty(&Interner)) + .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty)); + implicitly_sized_tys.map(move |self_ty| { + WhereClause::Implemented(TraitRef { + trait_id: sized_trait, + substitution: Substitution::from1(&Interner, self_ty.clone()), + }) + }) + }) } /// Resolve the default type params from generics diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 8db1592c916..4e761a6b474 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -398,15 +398,15 @@ fn test() { #[test] fn coerce_unsize_apit() { - // FIXME: #8984 check( r#" //- minicore: coerce_unsized trait Foo {} -fn test(f: impl Foo) { +fn test(f: impl Foo, g: &(impl Foo + ?Sized)) { let _: &dyn Foo = &f; - //^^ expected &dyn Foo, got &impl Foo + let _: &dyn Foo = g; + //^ expected &dyn Foo, got &impl Foo + ?Sized } "#, ); @@ -559,3 +559,16 @@ fn test() { "#, ); } + +#[test] +fn coerce_type_var() { + check_types( + r#" +//- minicore: from, coerce_unsized +fn test() { + let x = (); + let _: &() = &x.into(); +} //^^^^^^^^ () +"#, + ) +} diff --git a/crates/hir_ty/src/tests/display_source_code.rs b/crates/hir_ty/src/tests/display_source_code.rs index a484dc3a03b..240942e488d 100644 --- a/crates/hir_ty/src/tests/display_source_code.rs +++ b/crates/hir_ty/src/tests/display_source_code.rs @@ -44,13 +44,13 @@ fn main() { fn render_raw_ptr_impl_ty() { check_types_source_code( r#" -trait Sized {} +//- minicore: sized trait Unpin {} fn foo() -> *const (impl Unpin + Sized) { loop {} } fn main() { let foo = foo(); foo; -} //^^^ *const (impl Unpin + Sized) +} //^^^ *const impl Unpin "#, ); } @@ -67,3 +67,110 @@ fn foo(foo: &dyn for<'a> Foo<'a>) {} "#, ); } + +#[test] +fn sized_bounds_apit() { + check_types_source_code( + r#" +//- minicore: 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>, + ref_any: &impl ?Sized, + empty: impl, +) { + a; + //^ impl Foo + b; + //^ impl Foo + c; + //^ &impl Foo + ?Sized + d; + //^ S<impl Foo> + ref_any; + //^^^^^^^ &impl ?Sized + empty; +} //^^^^^ impl Sized +"#, + ); +} + +#[test] +fn sized_bounds_rpit() { + check_types_source_code( + r#" +//- minicore: sized +trait Foo {} +fn foo1() -> impl Foo { loop {} } +fn foo2() -> impl Foo + Sized { loop {} } +fn foo3() -> impl Foo + ?Sized { loop {} } +fn test() { + let foo = foo1(); + foo; + //^^^ impl Foo + let foo = foo2(); + foo; + //^^^ impl Foo + let foo = foo3(); + foo; +} //^^^ impl Foo + ?Sized +"#, + ); +} + +#[test] +fn parenthesize_ptr_rpit_sized_bounds() { + check_types_source_code( + r#" +//- minicore: sized +trait Foo {} +fn foo1() -> *const impl Foo { loop {} } +fn foo2() -> *const (impl Foo + Sized) { loop {} } +fn foo3() -> *const (impl Sized + Foo) { loop {} } +fn foo4() -> *const (impl Foo + ?Sized) { loop {} } +fn foo5() -> *const (impl ?Sized + Foo) { loop {} } +fn test() { + let foo = foo1(); + foo; + //^^^ *const impl Foo + let foo = foo2(); + foo; + //^^^ *const impl Foo + let foo = foo3(); + foo; + //^^^ *const impl Foo + let foo = foo4(); + foo; + //^^^ *const (impl Foo + ?Sized) + let foo = foo5(); + foo; +} //^^^ *const (impl Foo + ?Sized) +"#, + ); +} + +#[test] +fn sized_bounds_impl_traits_in_fn_signature() { + check_types_source_code( + r#" +//- minicore: 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 +"#, + ); +} diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 90eee77422e..0c502273099 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -922,6 +922,7 @@ fn test() { foo.call(); } fn super_trait_impl_return_trait_method_resolution() { check_infer( r#" + //- minicore: sized trait Base { fn foo(self) -> usize; } @@ -1312,3 +1313,29 @@ impl<'a, T> IntoIterator for &'a [T] { "#, ); } + +#[test] +fn sized_blanket_impl() { + check_infer( + r#" +//- minicore: sized +trait Foo { fn foo() -> u8; } +impl<T: Sized> Foo for T {} +fn f<S: Sized, T, U: ?Sized>() { + u32::foo; + S::foo; + T::foo; + U::foo; + <[u32]>::foo; +} +"#, + expect![[r#" + 89..160 '{ ...foo; }': () + 95..103 'u32::foo': fn foo<u32>() -> u8 + 109..115 'S::foo': fn foo<S>() -> u8 + 121..127 'T::foo': fn foo<T>() -> u8 + 133..139 'U::foo': {unknown} + 145..157 '<[u32]>::foo': {unknown} + "#]], + ); +} diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 58bbaa1c80e..2574dfca320 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -944,7 +944,7 @@ fn lifetime_from_chalk_during_deref() { r#" //- minicore: deref struct Box<T: ?Sized> {} -impl<T> core::ops::Deref for Box<T> { +impl<T: ?Sized> core::ops::Deref for Box<T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -1063,6 +1063,7 @@ fn cfg_tail() { fn impl_trait_in_option_9530() { check_types( r#" +//- minicore: sized struct Option<T>; impl<T> Option<T> { fn unwrap(self) -> T { loop {} } diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index d5d0173ec11..de1b0e9f298 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs @@ -43,7 +43,7 @@ fn test() { fn infer_desugar_async() { check_types( r#" -//- minicore: future +//- minicore: future, sized async fn foo() -> u64 { 128 } fn test() { @@ -147,7 +147,7 @@ mod ops { pub use self::try_trait::Try; } -mov convert { +mod convert { pub trait From<T> {} impl<T> From<T> for T {} } @@ -567,8 +567,8 @@ fn deref_trait() { check_types( r#" //- minicore: deref -struct Arc<T>; -impl<T> core::ops::Deref for Arc<T> { +struct Arc<T: ?Sized>; +impl<T: ?Sized> core::ops::Deref for Arc<T> { type Target = T; } @@ -589,9 +589,9 @@ fn deref_trait_with_inference_var() { check_types( r#" //- minicore: deref -struct Arc<T>; -fn new_arc<T>() -> Arc<T> { Arc } -impl<T> core::ops::Deref for Arc<T> { +struct Arc<T: ?Sized>; +fn new_arc<T: ?Sized>() -> Arc<T> { Arc } +impl<T: ?Sized> core::ops::Deref for Arc<T> { type Target = T; } @@ -631,7 +631,7 @@ fn deref_trait_with_question_mark_size() { check_types( r#" //- minicore: deref -struct Arc<T>; +struct Arc<T: ?Sized>; impl<T: ?Sized> core::ops::Deref for Arc<T> { type Target = T; } @@ -649,6 +649,25 @@ fn test(s: Arc<S>) { } #[test] +fn deref_trait_with_implicit_sized_requirement_on_inference_var() { + check_types( + r#" +//- minicore: deref +struct Foo<T>; +impl<T> core::ops::Deref for Foo<T> { + type Target = (); +} +fn test() { + let foo = Foo; + *foo; + //^^^^ () + let _: Foo<u8> = foo; +} +"#, + ) +} + +#[test] fn obligation_from_function_clause() { check_types( r#" @@ -845,6 +864,7 @@ fn test<T: ApplyL>(t: T) { fn argument_impl_trait() { check_infer_with_mismatches( r#" +//- minicore: sized trait Trait<T> { fn foo(&self) -> T; fn foo2(&self) -> i64; @@ -902,6 +922,7 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) { fn argument_impl_trait_type_args_1() { check_infer_with_mismatches( r#" +//- minicore: sized trait Trait {} trait Foo { // this function has an implicit Self param, an explicit type param, @@ -967,6 +988,7 @@ fn test() { fn argument_impl_trait_type_args_2() { check_infer_with_mismatches( r#" +//- minicore: sized trait Trait {} struct S; impl Trait for S {} @@ -1008,6 +1030,7 @@ fn test() { fn argument_impl_trait_to_fn_pointer() { check_infer_with_mismatches( r#" +//- minicore: sized trait Trait {} fn foo(x: impl Trait) { loop {} } struct S; @@ -1032,6 +1055,7 @@ fn test() { fn impl_trait() { check_infer( r#" +//- minicore: sized trait Trait<T> { fn foo(&self) -> T; fn foo2(&self) -> i64; @@ -1082,6 +1106,7 @@ fn simple_return_pos_impl_trait() { cov_mark::check!(lower_rpit); check_infer( r#" +//- minicore: sized trait Trait<T> { fn foo(&self) -> T; } @@ -1110,6 +1135,7 @@ fn test() { fn more_return_pos_impl_trait() { check_infer( r#" +//- minicore: sized trait Iterator { type Item; fn next(&mut self) -> Self::Item; @@ -1168,6 +1194,7 @@ fn test() { fn dyn_trait() { check_infer( r#" +//- minicore: sized trait Trait<T> { fn foo(&self) -> T; fn foo2(&self) -> i64; @@ -1217,6 +1244,7 @@ fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) { fn dyn_trait_in_impl() { check_infer( r#" +//- minicore: sized trait Trait<T, U> { fn foo(&self) -> (T, U); } @@ -1252,6 +1280,7 @@ fn test(s: S<u32, i32>) { fn dyn_trait_bare() { check_infer( r#" +//- minicore: sized trait Trait { fn foo(&self) -> u64; } @@ -1290,6 +1319,7 @@ fn test(x: Trait, y: &Trait) -> u64 { fn weird_bounds() { check_infer( r#" +//- minicore: sized trait Trait {} fn test( a: impl Trait + 'lifetime, @@ -1302,11 +1332,11 @@ fn test( "#, expect![[r#" 28..29 'a': impl Trait - 59..60 'b': impl + 59..60 'b': impl Sized 82..83 'c': impl Trait - 103..104 'd': impl - 128..129 'e': impl - 148..149 'f': impl Trait + 103..104 'd': impl Sized + 128..129 'e': impl ?Sized + 148..149 'f': impl Trait + ?Sized 173..175 '{}': () "#]], ); @@ -1331,6 +1361,7 @@ fn test(x: (impl Trait + UnknownTrait)) { fn assoc_type_bindings() { check_infer( r#" +//- minicore: sized trait Trait { type Type; } @@ -1495,6 +1526,7 @@ fn test<T: Trait1, U: Trait2>(x: T, y: U) { fn super_trait_impl_trait_method_resolution() { check_infer( r#" +//- minicore: sized mod foo { trait SuperTrait { fn foo(&self) -> u32 {} @@ -2299,6 +2331,7 @@ impl TokenStream for Rustc { fn unify_impl_trait() { check_infer_with_mismatches( r#" +//- minicore: sized trait Trait<T> {} fn foo(x: impl Trait<u32>) { loop {} } @@ -2417,8 +2450,8 @@ fn dyn_trait_through_chalk() { check_types( r#" //- minicore: deref -struct Box<T> {} -impl<T> core::ops::Deref for Box<T> { +struct Box<T: ?Sized> {} +impl<T: ?Sized> core::ops::Deref for Box<T> { type Target = T; } trait Trait { @@ -3515,3 +3548,33 @@ fn test() { "#, ); } + +#[test] +fn associated_type_sized_bounds() { + check_infer( + r#" +//- minicore: sized +struct Yes; +trait IsSized { const IS_SIZED: Yes; } +impl<T: Sized> IsSized for T { const IS_SIZED: Yes = Yes; } + +trait Foo { + type Explicit: Sized; + type Implicit; + type Relaxed: ?Sized; +} +fn f<F: Foo>() { + F::Explicit::IS_SIZED; + F::Implicit::IS_SIZED; + F::Relaxed::IS_SIZED; +} +"#, + expect![[r#" + 104..107 'Yes': Yes + 212..295 '{ ...ZED; }': () + 218..239 'F::Exp..._SIZED': Yes + 245..266 'F::Imp..._SIZED': Yes + 272..292 'F::Rel..._SIZED': {unknown} + "#]], + ); +} diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs index 076b2c8cba3..14b8a726a8f 100644 --- a/crates/hir_ty/src/utils.rs +++ b/crates/hir_ty/src/utils.rs @@ -13,7 +13,7 @@ use hir_def::{ intern::Interned, path::Path, resolver::{HasResolver, TypeNs}, - type_ref::TypeRef, + type_ref::{TraitBoundModifier, TypeRef}, AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, }; use hir_expand::name::{name, Name}; @@ -58,6 +58,10 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { }, WherePredicate::Lifetime { .. } => None, }) + .filter_map(|(path, bound_modifier)| match bound_modifier { + TraitBoundModifier::None => Some(path), + TraitBoundModifier::Maybe => None, + }) .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { Some(TypeNs::TraitId(t)) => Some(t), _ => None, diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index da023419ccf..f11eaaa546d 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -1226,7 +1226,9 @@ fn main() { #[test] fn hover_for_param_with_multiple_traits() { check( - r#"trait Deref { + r#" + //- minicore: sized + trait Deref { type Target: ?Sized; } trait DerefMut { @@ -3417,17 +3419,17 @@ fn foo() { fn hover_type_param() { check( r#" +//- minicore: sized struct Foo<T>(T); trait Copy {} trait Clone {} -trait Sized {} impl<T: Copy + Clone> Foo<T$0> where T: Sized {} "#, expect![[r#" *T* ```rust - T: Copy + Clone + Sized + T: Copy + Clone ``` "#]], ); @@ -3461,6 +3463,26 @@ impl<T: 'static> Foo<T$0> {} } #[test] + fn hover_type_param_not_sized() { + check( + r#" +//- minicore: sized +struct Foo<T>(T); +trait Copy {} +trait Clone {} +impl<T: Copy + Clone> Foo<T$0> where T: ?Sized {} +"#, + expect![[r#" + *T* + + ```rust + T: Copy + Clone + ?Sized + ``` + "#]], + ); + } + + #[test] fn hover_const_param() { check( r#" diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 3dc1bbf7dca..e3d63cc949e 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -924,9 +924,53 @@ fn main() { let foo = foo5(); // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 let foo = foo6(); - // ^^^ impl Fn(f64, f64) -> u32 + Sized + // ^^^ impl Fn(f64, f64) -> u32 + let foo = foo7(); + // ^^^ *const impl Fn(f64, f64) -> u32 +} +"#, + ) + } + + #[test] + fn fn_hints_ptr_rpit_fn_parentheses() { + check_types( + r#" +//- minicore: fn, sized +trait Trait {} + +fn foo1() -> *const impl Fn() { loop {} } +fn foo2() -> *const (impl Fn() + Sized) { loop {} } +fn foo3() -> *const (impl Fn() + ?Sized) { loop {} } +fn foo4() -> *const (impl Sized + Fn()) { loop {} } +fn foo5() -> *const (impl ?Sized + Fn()) { loop {} } +fn foo6() -> *const (impl Fn() + Trait) { loop {} } +fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} } +fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} } +fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} } +fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} } + +fn main() { + let foo = foo1(); + // ^^^ *const impl Fn() + let foo = foo2(); + // ^^^ *const impl Fn() + let foo = foo3(); + // ^^^ *const (impl Fn() + ?Sized) + let foo = foo4(); + // ^^^ *const impl Fn() + let foo = foo5(); + // ^^^ *const (impl Fn() + ?Sized) + let foo = foo6(); + // ^^^ *const (impl Fn() + Trait) let foo = foo7(); - // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized) + // ^^^ *const (impl Fn() + Trait) + let foo = foo8(); + // ^^^ *const (impl Fn() + Trait + ?Sized) + let foo = foo9(); + // ^^^ *const (impl Fn() -> u8 + ?Sized) + let foo = foo10(); + // ^^^ *const impl Fn() } "#, ) diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs index 7b8514efe52..63503392cbd 100644 --- a/crates/ide_assists/src/handlers/generate_function.rs +++ b/crates/ide_assists/src/handlers/generate_function.rs @@ -937,7 +937,8 @@ fn bar(worble: ()) ${0:-> ()} { fn add_function_with_impl_trait_arg() { check_assist( generate_function, - r" + r#" +//- minicore: sized trait Foo {} fn foo() -> impl Foo { todo!() @@ -945,8 +946,8 @@ fn foo() -> impl Foo { fn baz() { $0bar(foo()) } -", - r" +"#, + r#" trait Foo {} fn foo() -> impl Foo { todo!() @@ -958,7 +959,7 @@ fn baz() { fn bar(foo: impl Foo) ${0:-> ()} { todo!() } -", +"#, ) } |
