internal: add implicit Sized
bounds to type parameters.
This commit is contained in:
parent
8a8431133e
commit
3981373b93
@ -93,7 +93,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
|||||||
} else {
|
} else {
|
||||||
match &*data.ret_type {
|
match &*data.ret_type {
|
||||||
TypeRef::ImplTrait(bounds) => match bounds[0].as_ref() {
|
TypeRef::ImplTrait(bounds) => match bounds[0].as_ref() {
|
||||||
TypeBound::Path(path) => {
|
TypeBound::Path(path, _) => {
|
||||||
path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings
|
path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings
|
||||||
[0]
|
[0]
|
||||||
.type_ref
|
.type_ref
|
||||||
|
@ -338,10 +338,6 @@ fn add_where_predicate_from_bound(
|
|||||||
hrtb_lifetimes: Option<&Box<[Name]>>,
|
hrtb_lifetimes: Option<&Box<[Name]>>,
|
||||||
target: Either<TypeRef, LifetimeRef>,
|
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 bound = TypeBound::from_ast(lower_ctx, bound);
|
||||||
let predicate = match (target, bound) {
|
let predicate = match (target, bound) {
|
||||||
(Either::Left(type_ref), bound) => match hrtb_lifetimes {
|
(Either::Left(type_ref), bound) => match hrtb_lifetimes {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
generics::{GenericParams, TypeParamData, TypeParamProvenance},
|
generics::{GenericParams, TypeParamData, TypeParamProvenance},
|
||||||
type_ref::{LifetimeRef, TraitRef},
|
type_ref::{LifetimeRef, TraitBoundModifier, TraitRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
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 (ret_type, async_ret_type) = if func.async_token().is_some() {
|
||||||
let async_ret_type = ret_type.clone();
|
let async_ret_type = ret_type.clone();
|
||||||
let future_impl = desugar_future_path(ret_type);
|
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))
|
(TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type))
|
||||||
} else {
|
} else {
|
||||||
(ret_type, None)
|
(ret_type, None)
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
attr::RawAttrs,
|
attr::RawAttrs,
|
||||||
generics::{WherePredicate, WherePredicateTypeTarget},
|
generics::{WherePredicate, WherePredicateTypeTarget},
|
||||||
path::GenericArg,
|
path::GenericArg,
|
||||||
|
type_ref::TraitBoundModifier,
|
||||||
visibility::RawVisibility,
|
visibility::RawVisibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -543,7 +544,13 @@ fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match bound.as_ref() {
|
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) => {
|
TypeBound::ForLifetime(lifetimes, path) => {
|
||||||
w!(self, "for<{}> ", lifetimes.iter().format(", "));
|
w!(self, "for<{}> ", lifetimes.iter().format(", "));
|
||||||
self.print_path(path);
|
self.print_path(path);
|
||||||
|
@ -118,12 +118,20 @@ pub fn missing() -> LifetimeRef {
|
|||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum TypeBound {
|
pub enum TypeBound {
|
||||||
Path(Path),
|
Path(Path, TraitBoundModifier),
|
||||||
ForLifetime(Box<[Name]>, Path),
|
ForLifetime(Box<[Name]>, Path),
|
||||||
Lifetime(LifetimeRef),
|
Lifetime(LifetimeRef),
|
||||||
Error,
|
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 {
|
impl TypeRef {
|
||||||
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
||||||
pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
|
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) => {
|
TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
|
||||||
for bound in bounds {
|
for bound in bounds {
|
||||||
match bound.as_ref() {
|
match bound.as_ref() {
|
||||||
TypeBound::Path(path) | TypeBound::ForLifetime(_, path) => {
|
TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
|
||||||
go_path(path, f)
|
go_path(path, f)
|
||||||
}
|
}
|
||||||
TypeBound::Lifetime(_) | TypeBound::Error => (),
|
TypeBound::Lifetime(_) | TypeBound::Error => (),
|
||||||
@ -265,7 +273,7 @@ fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
|
|||||||
}
|
}
|
||||||
for bound in &binding.bounds {
|
for bound in &binding.bounds {
|
||||||
match bound.as_ref() {
|
match bound.as_ref() {
|
||||||
TypeBound::Path(path) | TypeBound::ForLifetime(_, path) => {
|
TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
|
||||||
go_path(path, f)
|
go_path(path, f)
|
||||||
}
|
}
|
||||||
TypeBound::Lifetime(_) | TypeBound::Error => (),
|
TypeBound::Lifetime(_) | TypeBound::Error => (),
|
||||||
@ -295,7 +303,11 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
|
|||||||
|
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
ast::TypeBoundKind::PathType(path_type) => {
|
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) => {
|
ast::TypeBoundKind::ForType(for_type) => {
|
||||||
let lt_refs = match for_type.generic_param_list() {
|
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> {
|
pub fn as_path(&self) -> Option<&Path> {
|
||||||
match self {
|
match self {
|
||||||
TypeBound::Path(p) | TypeBound::ForLifetime(_, p) => Some(p),
|
TypeBound::Path(p, _) | TypeBound::ForLifetime(_, p) => Some(p),
|
||||||
TypeBound::Lifetime(_) | TypeBound::Error => None,
|
TypeBound::Lifetime(_) | TypeBound::Error => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
intern::{Internable, Interned},
|
intern::{Internable, Interned},
|
||||||
item_scope::ItemInNs,
|
item_scope::ItemInNs,
|
||||||
path::{Path, PathKind},
|
path::{Path, PathKind},
|
||||||
type_ref::{TypeBound, TypeRef},
|
type_ref::{TraitBoundModifier, TypeBound, TypeRef},
|
||||||
visibility::Visibility,
|
visibility::Visibility,
|
||||||
AssocContainerId, Lookup, ModuleId, TraitId,
|
AssocContainerId, Lookup, ModuleId, TraitId,
|
||||||
};
|
};
|
||||||
@ -1026,7 +1026,14 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
|||||||
impl HirDisplay for TypeBound {
|
impl HirDisplay for TypeBound {
|
||||||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||||
match self {
|
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::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
|
||||||
TypeBound::ForLifetime(lifetimes, path) => {
|
TypeBound::ForLifetime(lifetimes, path) => {
|
||||||
write!(f, "for<{}> ", lifetimes.iter().format(", "))?;
|
write!(f, "for<{}> ", lifetimes.iter().format(", "))?;
|
||||||
|
@ -18,13 +18,14 @@
|
|||||||
generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
|
generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
|
||||||
path::{GenericArg, Path, PathSegment, PathSegments},
|
path::{GenericArg, Path, PathSegment, PathSegments},
|
||||||
resolver::{HasResolver, Resolver, TypeNs},
|
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,
|
AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId,
|
||||||
GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
|
GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
|
||||||
TypeAliasId, TypeParamId, UnionId, VariantId,
|
TypeAliasId, TypeParamId, UnionId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{name::Name, ExpandResult};
|
use hir_expand::{name::Name, ExpandResult};
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use stdx::impl_from;
|
use stdx::impl_from;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
@ -65,6 +66,8 @@ pub struct TyLoweringContext<'a> {
|
|||||||
/// Splitting this up would be a possible fix.
|
/// Splitting this up would be a possible fix.
|
||||||
opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>,
|
opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>,
|
||||||
expander: RefCell<Option<Expander>>,
|
expander: RefCell<Option<Expander>>,
|
||||||
|
/// Keeps tracking types with explicit `?Sized` bounds.
|
||||||
|
unsized_types: RefCell<FxHashSet<Ty>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TyLoweringContext<'a> {
|
impl<'a> TyLoweringContext<'a> {
|
||||||
@ -83,6 +86,7 @@ pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
|
|||||||
type_param_mode,
|
type_param_mode,
|
||||||
opaque_type_data,
|
opaque_type_data,
|
||||||
expander: RefCell::new(None),
|
expander: RefCell::new(None),
|
||||||
|
unsized_types: RefCell::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,17 +97,20 @@ pub fn with_debruijn<T>(
|
|||||||
) -> T {
|
) -> T {
|
||||||
let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
|
let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
|
||||||
let expander = self.expander.replace(None);
|
let expander = self.expander.replace(None);
|
||||||
|
let unsized_types = self.unsized_types.replace(Default::default());
|
||||||
let new_ctx = Self {
|
let new_ctx = Self {
|
||||||
in_binders: debruijn,
|
in_binders: debruijn,
|
||||||
impl_trait_counter: Cell::new(self.impl_trait_counter.get()),
|
impl_trait_counter: Cell::new(self.impl_trait_counter.get()),
|
||||||
opaque_type_data: RefCell::new(opaque_ty_data_vec),
|
opaque_type_data: RefCell::new(opaque_ty_data_vec),
|
||||||
expander: RefCell::new(expander),
|
expander: RefCell::new(expander),
|
||||||
|
unsized_types: RefCell::new(unsized_types),
|
||||||
..*self
|
..*self
|
||||||
};
|
};
|
||||||
let result = f(&new_ctx);
|
let result = f(&new_ctx);
|
||||||
self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
|
self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
|
||||||
self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
|
self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
|
||||||
self.expander.replace(new_ctx.expander.into_inner());
|
self.expander.replace(new_ctx.expander.into_inner());
|
||||||
|
self.unsized_types.replace(new_ctx.unsized_types.into_inner());
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,10 +785,27 @@ pub(crate) fn lower_type_bound(
|
|||||||
) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
|
) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
|
||||||
let mut bindings = None;
|
let mut bindings = None;
|
||||||
let trait_ref = match bound {
|
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 = self.lower_trait_ref_from_path(path, Some(self_ty));
|
||||||
bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
|
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) => {
|
TypeBound::ForLifetime(_, path) => {
|
||||||
// FIXME Don't silently drop the hrtb lifetimes here
|
// FIXME Don't silently drop the hrtb lifetimes here
|
||||||
bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
|
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,
|
trait_ref: TraitRef,
|
||||||
) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
|
) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
|
||||||
let last_segment = match bound {
|
let last_segment = match bound {
|
||||||
TypeBound::Path(path) | TypeBound::ForLifetime(_, path) => path.segments().last(),
|
TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => path.segments().last(),
|
||||||
TypeBound::Error | TypeBound::Lifetime(_) => None,
|
TypeBound::Path(_, TraitBoundModifier::Maybe)
|
||||||
|
| TypeBound::Error
|
||||||
|
| TypeBound::Lifetime(_) => None,
|
||||||
};
|
};
|
||||||
last_segment
|
last_segment
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -1053,10 +1079,40 @@ pub(crate) fn generic_predicates_query(
|
|||||||
let ctx =
|
let ctx =
|
||||||
TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
|
TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
|
||||||
let generics = generics(db.upcast(), def);
|
let generics = generics(db.upcast(), def);
|
||||||
resolver
|
|
||||||
|
let mut predicates = resolver
|
||||||
.where_predicates_in_scope()
|
.where_predicates_in_scope()
|
||||||
.flat_map(|pred| ctx.lower_where_predicate(pred, false).map(|p| make_binders(&generics, p)))
|
.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
|
/// Resolve the default type params from generics
|
||||||
|
Loading…
Reference in New Issue
Block a user