internal: add implicit Sized bounds to type parameters.

This commit is contained in:
Dawer 2021-06-06 23:41:15 +05:00
parent 8a8431133e
commit 3981373b93
7 changed files with 99 additions and 21 deletions

View File

@ -93,7 +93,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
} 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

View File

@ -338,10 +338,6 @@ fn add_where_predicate_from_bound(
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 {

View File

@ -10,7 +10,7 @@
use crate::{
generics::{GenericParams, TypeParamData, TypeParamProvenance},
type_ref::{LifetimeRef, TraitRef},
type_ref::{LifetimeRef, TraitBoundModifier, TraitRef},
};
use super::*;
@ -369,7 +369,7 @@ fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>>
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)

View File

@ -8,6 +8,7 @@
attr::RawAttrs,
generics::{WherePredicate, WherePredicateTypeTarget},
path::GenericArg,
type_ref::TraitBoundModifier,
visibility::RawVisibility,
};
@ -543,7 +544,13 @@ fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
}
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);

View File

@ -118,12 +118,20 @@ pub fn missing() -> 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 @@ fn go(type_ref: &TypeRef, f: &mut impl FnMut(&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 @@ fn go_path(path: &Path, f: &mut impl FnMut(&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 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
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 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
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,
}
}

View File

@ -13,7 +13,7 @@
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 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
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(", "))?;

View File

@ -18,13 +18,14 @@
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>>,
/// Keeps tracking types with explicit `?Sized` bounds.
unsized_types: RefCell<FxHashSet<Ty>>,
}
impl<'a> TyLoweringContext<'a> {
@ -83,6 +86,7 @@ pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
type_param_mode,
opaque_type_data,
expander: RefCell::new(None),
unsized_types: RefCell::default(),
}
}
@ -93,17 +97,20 @@ pub fn with_debruijn<T>(
) -> 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 @@ pub(crate) fn lower_type_bound(
) -> 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 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 @@ fn assoc_type_bindings_from_type_bound(
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()
@ -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::<Vec<_>>();
// 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