From 3981373b9389d8fb36de5b18f22b18c5f7aaa873 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Sun, 6 Jun 2021 23:41:15 +0500 Subject: [PATCH] internal: add implicit `Sized` bounds to type parameters. --- crates/hir/src/display.rs | 2 +- crates/hir_def/src/generics.rs | 4 -- crates/hir_def/src/item_tree/lower.rs | 4 +- crates/hir_def/src/item_tree/pretty.rs | 9 +++- crates/hir_def/src/type_ref.rs | 22 +++++++-- crates/hir_ty/src/display.rs | 11 ++++- crates/hir_ty/src/lower.rs | 68 +++++++++++++++++++++++--- 7 files changed, 99 insertions(+), 21 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 72f0d9b5f79..98c1f1c227c 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -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 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, ) { - 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/type_ref.rs b/crates/hir_def/src/type_ref.rs index 1d35f02ca1d..34abe9e0724 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,11 @@ 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() { @@ -322,7 +334,7 @@ impl TypeBound { pub fn as_path(&self) -> Option<&Path> { match self { - TypeBound::Path(p) | TypeBound::ForLifetime(_, p) => Some(p), + TypeBound::Path(p, _) | TypeBound::ForLifetime(_, p) => Some(p), TypeBound::Lifetime(_) | TypeBound::Error => None, } } diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 3f7455959d7..d074c19a3e4 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -13,7 +13,7 @@ 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, }; @@ -1026,7 +1026,14 @@ 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) => { + // todo don't print implicit Sized; implicit ?Sized on Self of a trait + 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/lower.rs b/crates/hir_ty/src/lower.rs index 92b376c4402..2eb9a21ddd2 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>, expander: RefCell>, + /// Keeps tracking types with explicit `?Sized` bounds. + unsized_types: RefCell>, } 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 } @@ -778,10 +785,27 @@ impl<'a> TyLoweringContext<'a> { ) -> impl Iterator + '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 none 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)); @@ -804,8 +828,10 @@ impl<'a> TyLoweringContext<'a> { trait_ref: TraitRef, ) -> impl Iterator + '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() @@ -1053,10 +1079,40 @@ 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::>(); + + // Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. + // Exception is Self of a trait. + let is_trait_def = matches!(def, GenericDefId::TraitId(..)); + let explicitly_unsized_tys = ctx.unsized_types.into_inner(); + let subtsts = generics.bound_vars_subst(DebruijnIndex::INNERMOST); + let generic_args = &subtsts.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)); + let sized_predicates = sized_trait + .into_iter() + .flat_map(|sized_trait| { + let implicitly_sized_tys = generic_args + .iter() + .filter_map(|generic_arg| generic_arg.ty(&Interner)) + .filter(|&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()), + }) + }) + }) + .map(|p| make_binders(&generics, crate::wrap_empty_binders(p))); + + predicates.extend(sized_predicates); + predicates.into() } /// Resolve the default type params from generics