diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 0d19dc25d402c..cbcb8a1e34548 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -578,7 +578,14 @@ pub(crate) struct RustcObjectLifetimeDefaultParser; impl NoArgsAttributeParser for RustcObjectLifetimeDefaultParser { const PATH: &[Symbol] = &[sym::rustc_object_lifetime_default]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::Trait), + Allow(Target::TyAlias), + Allow(Target::AssocTy), + ]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcObjectLifetimeDefault; } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 68fc7653b6368..ccce849e81a65 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -707,12 +707,12 @@ impl<'hir> GenericArgs<'hir> { } #[inline] - pub fn num_lifetime_params(&self) -> usize { + pub fn num_lifetime_args(&self) -> usize { self.args.iter().filter(|arg| matches!(arg, GenericArg::Lifetime(_))).count() } #[inline] - pub fn has_lifetime_params(&self) -> bool { + pub fn has_lifetime_args(&self) -> bool { self.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) } diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 3e687700f11c6..514b96a5b9395 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -787,11 +787,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }); match lifetime.kind { LifetimeKind::ImplicitObjectLifetimeDefault => { - // If the user does not write *anything*, we - // use the object lifetime defaulting - // rules. So e.g., `Box` becomes - // `Box`. - self.resolve_object_lifetime_default(&*lifetime) + // If the user doesn't write *anything*, we apply the + // trait object lifetime defaulting rules. + // E.g., `Box` becomes `Box`. + self.resolve_object_lifetime_default(&*lifetime); } LifetimeKind::Infer => { // If the user writes `'_`, we use the *ordinary* elision @@ -810,7 +809,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { hir::TyKind::Ref(lifetime_ref, ref mt) => { self.visit_lifetime(lifetime_ref); let scope = Scope::ObjectLifetimeDefault { - lifetime: self.rbv.defs.get(&lifetime_ref.hir_id.local_id).cloned(), + lifetime: self.rbv.defs.get(&lifetime_ref.hir_id.local_id).copied(), s: self.scope, }; self.with(scope, |this| this.visit_ty_unambig(mt.ty)); @@ -898,11 +897,58 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } + fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: HirId, _: Span) { + match qpath { + hir::QPath::Resolved(maybe_qself, path) => { + // Visit the path before the self type since computing the trait object lifetime + // default for the latter requires all lifetime arguments of the trait ref to be + // already resolved. + self.visit_path(path, id); + if let Some(qself) = maybe_qself { + // Read more about containers in fn `visit_path_segment_args`. + let container = match path.res { + Res::Def(DefKind::AssocTy, def_id) => Some(( + self.tcx.parent(def_id), + &path.segments[..path.segments.len() - 1], + )), + _ => None, + }; + let object_lifetime_defaults = + container.map_or(Vec::new(), |(def_id, segs)| { + let generics = self.tcx.generics_of(def_id); + self.compute_object_lifetime_defaults(generics, segs) + }); + if let Some(<) = object_lifetime_defaults.get(0) { + let scope = Scope::ObjectLifetimeDefault { lifetime: lt, s: self.scope }; + self.with(scope, |this| this.visit_ty_unambig(qself)); + } else { + self.visit_ty_unambig(qself); + } + } + } + hir::QPath::TypeRelative(qself, segment) => { + // Computing the trait object lifetime defaults that are induced by type-relative + // paths would require full type-dependent resolution as performed by HIR ty + // lowering whose results we don't have access to here (esp. in ItemCtxts which + // don't "persist" any resolutions during lowering). + // For maximum forward compatibility, in ItemCtxts we make HIR ty lowering reject + // implicit trait object lifetime bounds inside such paths on grounds of + // the default being *indeterminate*. + // FIXME: Figure out if there's a feasible way to obtain the map of type-dependent + // definitions here / interleave RBV and HIR ty lowering. + let scope = Scope::ObjectLifetimeDefault { lifetime: None, s: self.scope }; + self.with(scope, |this| { + this.visit_ty_unambig(qself); + this.visit_path_segment(segment) + }); + } + } + } + fn visit_path(&mut self, path: &hir::Path<'tcx>, hir_id: HirId) { - for (i, segment) in path.segments.iter().enumerate() { - let depth = path.segments.len() - i - 1; + for (index, segment) in path.segments.iter().enumerate() { if let Some(args) = segment.args { - self.visit_segment_args(path.res, depth, args); + self.visit_path_segment_args(args, index, path); } } if let Res::Def(DefKind::TyParam | DefKind::ConstParam, param_def_id) = path.res { @@ -1046,54 +1092,57 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectLifetimeDefault { - debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam); - let hir::Node::GenericParam(param) = tcx.hir_node_by_def_id(param_def_id) else { - bug!("expected GenericParam for object_lifetime_default"); - }; - match param.source { - hir::GenericParamSource::Generics => { - let parent_def_id = tcx.local_parent(param_def_id); - let generics = tcx.hir_get_generics(parent_def_id).unwrap(); - let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id); - let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap(); - - // Scan the bounds and where-clauses on parameters to extract bounds - // of the form `T:'a` so as to determine the `ObjectLifetimeDefault` - // for each type parameter. - match param.kind { + // Scan the bounds and where-clauses on parameters to extract bounds of the form `T: 'a` + // so as to determine the `ObjectLifetimeDefault` for each type parameter. + + let Ok((generics, bounds)) = (match tcx.hir_node_by_def_id(param_def_id) { + hir::Node::GenericParam(param) => match param.source { + hir::GenericParamSource::Generics => match param.kind { GenericParamKind::Type { .. } => { - let mut set = Set1::Empty; - - // Look for `type: ...` where clauses. - for bound in generics.bounds_for_param(param_def_id) { - // Ignore `for<'a> type: ...` as they can change what - // lifetimes mean (although we could "just" handle it). - if !bound.bound_generic_params.is_empty() { - continue; - } + Ok((tcx.hir_get_generics(tcx.local_parent(param_def_id)).unwrap(), &[][..])) + } + _ => Err(()), + }, + hir::GenericParamSource::Binder => return ObjectLifetimeDefault::Empty, + }, + // For `Self` type parameters + hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Trait(_, _, _, _, generics, bounds, _), + .. + }) => Ok((generics, bounds)), + _ => Err(()), + }) else { + bug!("`object_lifetime_default` must only be called on type parameters") + }; - for bound in bound.bounds { - if let hir::GenericBound::Outlives(lifetime) = bound { - set.insert(lifetime.kind); - } - } - } + let mut set = Set1::Empty; - match set { - Set1::Empty => ObjectLifetimeDefault::Empty, - Set1::One(hir::LifetimeKind::Static) => ObjectLifetimeDefault::Static, - Set1::One(hir::LifetimeKind::Param(param_def_id)) => { - ObjectLifetimeDefault::Param(param_def_id.to_def_id()) - } - _ => ObjectLifetimeDefault::Ambiguous, - } - } - _ => { - bug!("object_lifetime_default_raw must only be called on a type parameter") - } + let mut add_outlives_bounds = |bounds: &[hir::GenericBound<'_>]| { + for bound in bounds { + if let hir::GenericBound::Outlives(lifetime) = bound { + set.insert(lifetime.kind); } } - hir::GenericParamSource::Binder => ObjectLifetimeDefault::Empty, + }; + + add_outlives_bounds(bounds); + + // Look for `Type: ...` where clauses. + for bound in generics.bounds_for_param(param_def_id) { + // Ignore `for<'a> Type: ...` as they can change what + // lifetimes mean (although we could "just" handle it). + if bound.bound_generic_params.is_empty() { + add_outlives_bounds(&bound.bounds); + } + } + + match set { + Set1::Empty => ObjectLifetimeDefault::Empty, + Set1::One(hir::LifetimeKind::Static) => ObjectLifetimeDefault::Static, + Set1::One(hir::LifetimeKind::Param(param_def_id)) => { + ObjectLifetimeDefault::Param(param_def_id.to_def_id()) + } + _ => ObjectLifetimeDefault::Ambiguous, } } @@ -1668,129 +1717,70 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn visit_segment_args( + fn visit_path_segment_args( &mut self, - res: Res, - depth: usize, generic_args: &'tcx hir::GenericArgs<'tcx>, + index: usize, + path: &hir::Path<'tcx>, ) { if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { self.visit_fn_like_elision(inputs, Some(output), false); return; } + // Let's first resolve all lifetime arguments because we need their + // resolution for computing the trait object lifetime defaults. for arg in generic_args.args { if let hir::GenericArg::Lifetime(lt) = arg { self.visit_lifetime(lt); } } - // Figure out if this is a type/trait segment, - // which requires object lifetime defaults. - let type_def_id = match res { - Res::Def(DefKind::AssocTy, def_id) if depth == 1 => Some(self.tcx.parent(def_id)), - Res::Def(DefKind::Variant, def_id) if depth == 0 => Some(self.tcx.parent(def_id)), + // Figure out if this is an eligible container that induces lifetime defaults for + // trait object types contained in any of the type arguments passed to it + // (any inner containers will of course end up shadowing that default). + // + // FIXME(mgca, #151649): Assoc (and free?) consts should also qualify. + // FIXME(return_type_notation, #151662): Assoc fns should also qualify. + let depth = path.segments.len() - index - 1; + let container = match path.res { + Res::Def(DefKind::AssocTy, def_id) if depth == 1 => { + Some((self.tcx.parent(def_id), &path.segments[..=index])) + } + Res::Def(DefKind::Variant, def_id) if depth == 0 => { + Some((self.tcx.parent(def_id), path.segments)) + } Res::Def( DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias | DefKind::Trait - | DefKind::TraitAlias, + | DefKind::TraitAlias + | DefKind::AssocTy, def_id, - ) if depth == 0 => Some(def_id), + ) if depth == 0 => Some((def_id, path.segments)), + // Note: We don't need to care about definition kinds that may have generics if they + // can only ever appear in positions where we can perform type inference (i.e., bodies). _ => None, }; - debug!(?type_def_id); + debug!(?container); - // Compute a vector of defaults, one for each type parameter, - // per the rules given in RFCs 599 and 1156. Example: - // - // ```rust - // struct Foo<'a, T: 'a, U> { } - // ``` - // - // If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default - // `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound) - // and `dyn Baz` to `dyn Baz + 'static` (because there is no - // such bound). - // - // Therefore, we would compute `object_lifetime_defaults` to a - // vector like `['x, 'static]`. Note that the vector only - // includes type parameters. - let object_lifetime_defaults = type_def_id.map_or_else(Vec::new, |def_id| { - let in_body = { - let mut scope = self.scope; - loop { - match *scope { - Scope::Root { .. } => break false, - - Scope::Body { .. } => break true, - - Scope::Binder { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } - | Scope::Opaque { s, .. } - | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } - | Scope::LateBoundary { s, .. } => { - scope = s; - } - } - } - }; - - let rbv = &self.rbv; - let generics = self.tcx.generics_of(def_id); - - // `type_def_id` points to an item, so there is nothing to inherit generics from. - debug_assert_eq!(generics.parent_count, 0); - - let set_to_region = |set: ObjectLifetimeDefault| match set { - ObjectLifetimeDefault::Empty => { - if in_body { - None - } else { - Some(ResolvedArg::StaticLifetime) - } - } - ObjectLifetimeDefault::Static => Some(ResolvedArg::StaticLifetime), - ObjectLifetimeDefault::Param(param_def_id) => { - // This index can be used with `generic_args` since `parent_count == 0`. - let index = generics.param_def_id_to_index[¶m_def_id] as usize; - generic_args.args.get(index).and_then(|arg| match arg { - GenericArg::Lifetime(lt) => rbv.defs.get(<.hir_id.local_id).copied(), - _ => None, - }) - } - ObjectLifetimeDefault::Ambiguous => None, - }; - generics - .own_params - .iter() - .filter_map(|param| { - match self.tcx.def_kind(param.def_id) { - // Generic consts don't impose any constraints. - // - // We still store a dummy value here to allow generic parameters - // in an arbitrary order. - DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty), - DefKind::TyParam => Some(self.tcx.object_lifetime_default(param.def_id)), - // We may also get a `Trait` or `TraitAlias` because of how generics `Self` parameter - // works. Ignore it because it can't have a meaningful lifetime default. - DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None, - dk => bug!("unexpected def_kind {:?}", dk), - } - }) - .map(set_to_region) - .collect() - }); + let (has_self, object_lifetime_defaults) = container + .map(|(def_id, segs)| { + let generics = self.tcx.generics_of(def_id); + let defaults = self.compute_object_lifetime_defaults(generics, segs); + (generics.has_own_self(), defaults) + }) + .unwrap_or_default(); debug!(?object_lifetime_defaults); - let mut i = 0; + let mut i = has_self as usize; for arg in generic_args.args { match arg { + // We've already visited all lifetime arguments at the start. GenericArg::Lifetime(_) => {} GenericArg::Type(ty) => { if let Some(<) = object_lifetime_defaults.get(i) { @@ -1812,38 +1802,43 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } - // Hack: When resolving the type `XX` in an assoc ty binding like - // `dyn Foo<'b, Item = XX>`, the current object-lifetime default - // would be to examine the trait `Foo` to check whether it has - // a lifetime bound declared on `Item`. e.g., if `Foo` is - // declared like so, then the default object lifetime bound in - // `XX` should be `'b`: - // - // ```rust - // trait Foo<'a> { - // type Item: 'a; - // } - // ``` - // - // but if we just have `type Item;`, then it would be - // `'static`. However, we don't get all of this logic correct. - // - // Instead, we do something hacky: if there are no lifetime parameters - // to the trait, then we simply use a default object lifetime - // bound of `'static`, because there is no other possibility. On the other hand, - // if there ARE lifetime parameters, then we require the user to give an - // explicit bound for now. - // - // This is intended to leave room for us to implement the - // correct behavior in the future. - let has_lifetime_parameter = - generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); + let has_lifetime_args = generic_args.has_lifetime_args(); - // Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or - // in the trait ref `YY<...>` in `Item: YY<...>`. for constraint in generic_args.constraints { let scope = Scope::ObjectLifetimeDefault { - lifetime: if has_lifetime_parameter { + // FIXME: Ideally we would consider the *item bounds* of assoc types when deducing + // the trait object lifetime default for the RHS of assoc type bindings. + // For example, given + // + // trait TraitA<'a> { type AssocTy: ?Sized + 'a; } + // trait TraitB { type AssocTy<'a>: ?Sized + 'a; } + // + // we would elaborate the `dyn Bound` in `TraitA<'r, AssocTy = dyn Bound>` + // and `TraitB = dyn Bound>` to `dyn Bound + 'r`. + // + // FIXME: Moreover, ideally GAT args in bindings could induce + // trait object lifetime defaults. For example, given + // + // trait TraitA<'a> { type AssocTy; } + // trait TraitB { type AssocTy<'a, T: ?Sized + 'a>; } + // + // we would elab the `dyn Bound` in `TraitA<'r, AssocTy = ()>` + // and `TraitB = ()>` to `dyn Bound + 'r`. + // + // HACK: For now however, if the user passes any lifetime arguments to the trait or + // the (generic) assoc type, we will treat the trait object lifetime default + // as indeterminate thus forcing the user to explicitly specify the lifetime. + // + // If the trait or the assoc type have generic parameters, it's *possible* + // that they occur in the predicates or item bounds of the assoc type, so we + // conservatively reject such cases to allow us to implement the correct + // behavior in the future (here we assume that the number of arguments equals + // the number of parameters which is fine since a mismatch would get rejected + // later anyway). + // + // If the items don't have any lifetime parameters we can safely use `'static` + // since there is no other possibility. + lifetime: if has_lifetime_args || constraint.gen_args.has_lifetime_args() { None } else { Some(ResolvedArg::StaticLifetime) @@ -1865,11 +1860,12 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // `for<'a> for<'r> >::x::<'r, T>::{opaque#0}: for<'b> Other<'b>`. if constraint.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation { - let bound_vars = if let Some(type_def_id) = type_def_id - && let DefKind::Trait | DefKind::TraitAlias = self.tcx.def_kind(type_def_id) + let bound_vars = if let Some((container_def_id, _)) = container + && let DefKind::Trait | DefKind::TraitAlias = + self.tcx.def_kind(container_def_id) && let Some((mut bound_vars, assoc_fn)) = BoundVarContext::supertrait_hrtb_vars( self.tcx, - type_def_id, + container_def_id, constraint.ident, ty::AssocTag::Fn, ) { @@ -1898,10 +1894,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { this.visit_assoc_item_constraint(constraint) }); }); - } else if let Some(type_def_id) = type_def_id { + } else if let Some((container_def_id, _)) = container { let bound_vars = BoundVarContext::supertrait_hrtb_vars( self.tcx, - type_def_id, + container_def_id, constraint.ident, ty::AssocTag::Type, ) @@ -1919,6 +1915,114 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } + /// Compute a list of trait object lifetime defaults, one for each type parameter, + /// per the rules initially given in RFCs [599] and [1156]. Example: + /// + /// ``` + /// struct Foo<'a, T: 'a + ?Sized, U: ?Sized>(&'a T, &'a U); + /// ``` + /// + /// If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to elaborate + /// * `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound) and + /// * `dyn Baz` to `dyn Baz + 'static` (because there is no such bound). + /// + /// Therefore, we would compute a list like `['x, 'static]`. Note that the list only + /// includes entries for type and const parameters, not for lifetime parameters. + /// + /// [599]: https://rust-lang.github.io/rfcs/0599-default-object-bound.html + /// [1156]: https://rust-lang.github.io/rfcs/1156-adjust-default-object-bounds.html + fn compute_object_lifetime_defaults( + &self, + generics: &ty::Generics, + segments: &[hir::PathSegment<'_>], + ) -> Vec> { + let in_body = { + let mut scope = self.scope; + loop { + match *scope { + Scope::Root { .. } => break false, + + Scope::Body { .. } => break true, + + Scope::Binder { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Opaque { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } + | Scope::LateBoundary { s, .. } => { + scope = s; + } + } + } + }; + + let set_to_region = |set: ObjectLifetimeDefault| match set { + ObjectLifetimeDefault::Empty => { + if in_body { + None + } else { + Some(ResolvedArg::StaticLifetime) + } + } + ObjectLifetimeDefault::Static => Some(ResolvedArg::StaticLifetime), + ObjectLifetimeDefault::Param(param_def_id) => { + fn param_to_depth_and_index( + generics: &ty::Generics, + tcx: TyCtxt<'_>, + def_id: DefId, + ) -> (usize, usize) { + if let Some(&index) = generics.param_def_id_to_index.get(&def_id) { + let has_self = generics.has_own_self(); + (0, index as usize - generics.parent_count - has_self as usize) + } else if let Some(parent) = generics.parent { + let parent = tcx.generics_of(parent); + let (depth, index) = param_to_depth_and_index(parent, tcx, def_id); + (depth + 1, index) + } else { + unreachable!() + } + } + + let (depth, index) = param_to_depth_and_index(generics, self.tcx, param_def_id); + segments[segments.len() - depth - 1] + .args + .and_then(|args| args.args.get(index)) + .and_then(|arg| match arg { + GenericArg::Lifetime(lt) => self.rbv.defs.get(<.hir_id.local_id).copied(), + _ => None, + }) + } + ObjectLifetimeDefault::Ambiguous => None, + }; + generics + .own_params + .iter() + .filter_map(|param| { + // NB: `Self` type params share the `DefId` with the corresponding trait (alias). + // + // Since trait aliases can't be used as the qself of fully qualified paths, the + // trait object lifetime default for their `Self` type param is never needed. + // Thus, we don't even try to compute it. + // + // We still need to map const params & trait aliases to *some* default to make it + // easy & predictable for the caller how to map the defaults back to generic args. + // As they can't tell if a given inferred arg refers to a type or a const at this + // stage of analysis, they can't skip it and thus we need to provide (dummy) + // defaults for const args. Otherwise, they wouldn't properly align. + + match self.tcx.def_kind(param.def_id) { + DefKind::TyParam | DefKind::Trait => { + Some(self.tcx.object_lifetime_default(param.def_id)) + } + DefKind::ConstParam | DefKind::TraitAlias => Some(ObjectLifetimeDefault::Empty), + DefKind::LifetimeParam => None, + kind => bug!("unexpected def kind {kind:?}"), + } + }) + .map(set_to_region) + .collect() + } + /// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the /// associated type name and starting trait. /// For example, imagine we have @@ -2077,7 +2181,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // `for<'a, 'b, 'r> >::x::<'r, T>::{opaque#0}: Other<'b>`. // // We handle this similarly for associated-type-bound style return-type-notation - // in `visit_segment_args`. + // in `visit_path_segment_args`. fn try_append_return_type_notation_params( &mut self, hir_id: HirId, diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 2b7854769b426..ccad1649f2c9d 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -180,7 +180,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { AngleBrackets::Missing => 0, // Only lifetime arguments can be implied AngleBrackets::Implied => self.gen_args.args.len(), - AngleBrackets::Available => self.gen_args.num_lifetime_params(), + AngleBrackets::Available => self.gen_args.num_lifetime_args(), } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index ff0a5a8df0faf..05605b010efdc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -421,7 +421,7 @@ pub(crate) fn check_generic_arg_count( let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; let named_const_param_count = param_counts.consts; let infer_lifetimes = - (gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_params(); + (gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_args(); if gen_pos != GenericArgPosition::Type && let Some(c) = gen_args.constraints.first() @@ -471,7 +471,7 @@ pub(crate) fn check_generic_arg_count( let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; let max_expected_lifetime_args = param_counts.lifetimes; - let num_provided_lifetime_args = gen_args.num_lifetime_params(); + let num_provided_lifetime_args = gen_args.num_lifetime_args(); let lifetimes_correct = check_lifetime_args( min_expected_lifetime_args, @@ -595,7 +595,7 @@ pub(crate) fn check_generic_arg_count( - default_counts.consts }; debug!(?expected_min); - debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); + debug!(arg_counts.lifetimes=?gen_args.num_lifetime_args()); let provided = gen_args.num_generic_params(); @@ -605,7 +605,7 @@ pub(crate) fn check_generic_arg_count( named_const_param_count + named_type_param_count + synth_type_param_count, provided, param_counts.lifetimes + has_self as usize, - gen_args.num_lifetime_params(), + gen_args.num_lifetime_args(), ) }; @@ -628,7 +628,7 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( let param_counts = def.own_counts(); if let Some(span_late) = def.has_late_bound_regions - && args.has_lifetime_params() + && args.has_lifetime_args() { let msg = "cannot specify lifetime arguments explicitly \ if late bound lifetime parameters are present"; @@ -636,7 +636,7 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( let span = args.args[0].span(); if position == GenericArgPosition::Value - && args.num_lifetime_params() != param_counts.lifetimes + && args.num_lifetime_args() != param_counts.lifetimes { struct_span_code_err!(cx.dcx(), span, E0794, "{}", msg) .with_span_note(span_late, note) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 4988cafdd3637..ecc84267cb843 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1527,7 +1527,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Some(name) = tcx.intrinsic(def_id) { record!(self.tables.intrinsic[def_id] <- name); } - if let DefKind::TyParam = def_kind { + if let DefKind::TyParam | DefKind::Trait = def_kind { let default = self.tcx.object_lifetime_default(def_id); record!(self.tables.object_lifetime_default[def_id] <- default); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2287b8bf483f6..651715b68f63a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -758,20 +758,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Debugging aid for `object_lifetime_default` query. fn check_object_lifetime_default(&self, hir_id: HirId) { let tcx = self.tcx; - if let Some(owner_id) = hir_id.as_owner() - && let Some(generics) = tcx.hir_get_generics(owner_id.def_id) - { - for p in generics.params { - let hir::GenericParamKind::Type { .. } = p.kind else { continue }; - let default = tcx.object_lifetime_default(p.def_id); - let repr = match default { - ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(), - ObjectLifetimeDefault::Static => "'static".to_owned(), - ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), - ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), - }; - tcx.dcx().emit_err(errors::ObjectLifetimeErr { span: p.span, repr }); - } + let Some(owner_id) = hir_id.as_owner() else { return }; + for param in &tcx.generics_of(owner_id.def_id).own_params { + let ty::GenericParamDefKind::Type { .. } = param.kind else { continue }; + let default = tcx.object_lifetime_default(param.def_id); + let repr = match default { + ObjectLifetimeDefault::Empty => "Empty".to_owned(), + ObjectLifetimeDefault::Static => "'static".to_owned(), + ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), + ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), + }; + tcx.dcx() + .emit_err(errors::ObjectLifetimeErr { span: tcx.def_span(param.def_id), repr }); } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c09e17d3787e1..5664842d349a2 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1989,6 +1989,9 @@ impl<'tcx> ContainerTy<'_, 'tcx> { match self { Self::Ref(region) => ObjectLifetimeDefault::Arg(region), Self::Regular { ty: container, args, arg: index } => { + // FIXME(fmease): Since #129543 assoc tys can now also induce trait object + // lifetime defaults. Re-elide these, too! + let (DefKind::Struct | DefKind::Union | DefKind::Enum diff --git a/tests/ui/deriving/issue-89188-gat-hrtb.rs b/tests/ui/deriving/issue-89188-gat-hrtb.rs index a7b43159f16fd..7cedae4ef61f0 100644 --- a/tests/ui/deriving/issue-89188-gat-hrtb.rs +++ b/tests/ui/deriving/issue-89188-gat-hrtb.rs @@ -23,7 +23,7 @@ trait Trait<'s, 't, 'u> {} #[derive(Clone)] struct ShimMethod3( pub &'static dyn for<'s> Fn( - &'s mut T::Shim Fn(&'s mut T::Shim Trait<'s, 't, 'u>>)>, + &'s mut T::Shim Fn(&'s mut T::Shim Trait<'s, 't, 'u> + 's>) + 's>, ), ); diff --git a/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr b/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr index af0a9d064d571..a7966f87ad379 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.edition2015.stderr @@ -193,20 +193,16 @@ help: if this is a dyn-compatible trait, use `dyn` LL | type H = (u8)>::Output; | ++++ + -error[E0223]: ambiguous associated type +error[E0228]: cannot deduce the lifetime bound for this trait object type from context --> $DIR/bad-assoc-ty.rs:37:10 | LL | type H = Fn(u8) -> (u8)::Output; - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: use fully-qualified syntax - | -LL - type H = Fn(u8) -> (u8)::Output; -LL + type H = <(dyn Fn(u8) -> u8 + 'static) as BitOr>::Output; + | ^^^^^^^^^^^^^^ | -LL - type H = Fn(u8) -> (u8)::Output; -LL + type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output; +help: please supply an explicit bound | +LL | type H = Fn(u8) -> (u8) + /* 'a */::Output; + | ++++++++++ error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:44:19 @@ -310,5 +306,5 @@ LL | fn foo(_: F) where F: Fn() -> _ {} error: aborting due to 30 previous errors; 1 warning emitted -Some errors have detailed explanations: E0121, E0223, E0740. +Some errors have detailed explanations: E0121, E0223, E0228, E0740. For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/did_you_mean/bad-assoc-ty.rs b/tests/ui/did_you_mean/bad-assoc-ty.rs index 39f0a84855af7..531e1846e04fb 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.rs +++ b/tests/ui/did_you_mean/bad-assoc-ty.rs @@ -35,10 +35,10 @@ type G = dyn 'static + (Send)::AssocTy; // This is actually a legal path with fn-like generic arguments in the middle! // Recovery should not apply in this context. type H = Fn(u8) -> (u8)::Output; -//[edition2015]~^ ERROR ambiguous associated type +//[edition2021]~^ ERROR expected a type, found a trait +//[edition2015]~^^ ERROR cannot deduce the lifetime bound for this trait object type from context //[edition2015]~| WARN trait objects without an explicit `dyn` are deprecated //[edition2015]~| WARN this is accepted in the current edition -//[edition2021]~^^^^ ERROR expected a type, found a trait macro_rules! ty { ($ty: ty) => ($ty::AssocTy); diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs new file mode 100644 index 0000000000000..a7ab2d3dca4e9 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs @@ -0,0 +1,33 @@ +// Ideally, given an assoc type binding `dyn Trait`, we'd factor in the item bounds of +// assoc type `AssocTy` when computing the trait object lifetime default for type `Ty`. +// +// However, since the current implementation can't handle this we instead conservatively and hackily +// treat the trait object lifetime default of the RHS as indeterminate if any lifetime arguments are +// passed to the trait ref (or the GAT) thus rejecting any implicit trait object lifetime bounds. +// This way, we can still implement the desired behavior in the future. + +trait Foo<'a> { + type Item: 'a + ?Sized; + + fn item(&self) -> Box { panic!() } +} + +trait Bar {} + +impl Foo<'_> for T { + type Item = dyn Bar; +} + +fn is_static(_: T) where T: 'static {} + +// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'a` instead of rejecting it. +fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context + +fn main() { + let s = format!("foo"); + let r = bar(&s); + + // If it weren't for the conservative path above, we'd expect an error here. + is_static(r.item()); +} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.stderr b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.stderr new file mode 100644 index 0000000000000..2e9c415548b92 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.stderr @@ -0,0 +1,14 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds-non-static.rs:24:50 + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } + | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar + /* 'a */> { &() } + | ++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.rs new file mode 100644 index 0000000000000..0899ab08dd9dc --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.rs @@ -0,0 +1,37 @@ +// Ideally, given an assoc type binding `dyn Trait`, we'd factor in the item bounds of +// assoc type `AssocTy` when computing the trait object lifetime default for type `Ty`. +// +// However, since the current implementation can't handle this we instead conservatively and hackily +// treat the trait object lifetime default of the RHS as indeterminate if any lifetime arguments are +// passed to the trait ref (or the GAT) thus rejecting any implicit trait object lifetime bounds. +// This way, we can still implement the desired behavior in the future. + +trait Foo<'a> { + type Item: ?Sized; + + fn item(&self) -> Box { panic!() } +} + +trait Bar {} + +impl Foo<'_> for T { + type Item = dyn Bar; +} + +fn is_static(_: T) where T: 'static {} + +// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'static` instead of rejecting it. +fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context + +// FIXME: Ideally, we'd elaborate `dyn Bar` to `dyn Bar + 'static` instead of rejecting it. +fn baz(x: &str) -> &dyn Foo { &() } +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context + +fn main() { + let s = format!("foo"); + let r = bar(&s); + is_static(r.item()); + let r = baz(&s); + is_static(r.item()); +} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.stderr b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.stderr new file mode 100644 index 0000000000000..f47326297e58f --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-binding-item-bounds.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds.rs:24:50 + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } + | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar + /* 'a */> { &() } + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-assoc-ty-binding-item-bounds.rs:28:36 + | +LL | fn baz(x: &str) -> &dyn Foo { &() } + | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn baz(x: &str) -> &dyn Foo { &() } + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs new file mode 100644 index 0000000000000..67d5b8b56d811 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty-static.rs @@ -0,0 +1,19 @@ +// Check that resolved associated type paths induce the correct +// trait object lifetime default for the self type. + +//@ check-pass + +trait Outer { type Ty; } +trait Inner {} + +impl<'a> Outer for dyn Inner + 'a { type Ty = &'a (); } + +// We deduce `dyn Inner + 'static` from absence of any bounds on self ty param of trait `Outer`. +// +// Prior to PR rust-lang/rust#129543, assoc tys weren't considered eligible *containers* and +// thus we'd use the *trait object lifetime default* induced by the reference type ctor `&`, +// namely `'r`. Now however, the assoc ty overrides that default to be `'static`. +fn f<'r>(x: &'r ::Ty) { /*check*/ g(x) } +fn g<'r>(x: &'r ::Ty) {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs new file mode 100644 index 0000000000000..bdf05338894d1 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-assoc-ty-self-ty.rs @@ -0,0 +1,19 @@ +// Check that resolved associated type paths induce the correct +// trait object lifetime default for the self type. + +//@ check-pass +//@ revisions: bound clause + +// RBV works on the HIR where where-clauses and item bounds of traits aren't merged yet. +// It's therefore wise to check both forms and make sure both are treated the same by RBV. +#[cfg(bound)] trait Outer<'a>: 'a { type Ty; } +#[cfg(clause)] trait Outer<'a> where Self: 'a { type Ty; } +trait Inner {} + +impl<'a> Outer<'a> for dyn Inner + 'a { type Ty = &'a (); } + +fn f<'r>(x: >::Ty) { /*check*/ g(x) } +// We deduce `dyn Inner + 'r` from bound `'a` on self ty param of trait `Outer`. +fn g<'r>(x: >::Ty) {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.rs b/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.rs new file mode 100644 index 0000000000000..723d23f86d485 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.rs @@ -0,0 +1,26 @@ +// Ideally, given an assoc type binding `dyn Trait`, we'd factor in the item bounds of +// assoc type `AssocTy` when computing the trait object lifetime default for type `Ty`. +// +// However, since the current implementation can't handle this we instead conservatively and hackily +// treat the trait object lifetime default of the RHS as indeterminate if any lifetime arguments are +// passed to (the trait ref or) the GAT thus rejecting any implicit trait object lifetime bounds. +// This way, we can still implement the desired behavior in the future. + +trait Outer { + type A<'r>: ?Sized; + type B<'r>: ?Sized + 'r; +} + +trait Inner {} + +// FIXME: Ideally, we'd elaborate `dyn Inner` to `dyn Inner + 'static` here instead of rejecting it. +fn f0<'r>(x: impl Outer = dyn Inner>) { /*check*/ g0(x) } +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context +fn g0<'r>(_: impl Outer = dyn Inner + 'static>) {} + +fn f1<'r>(x: impl Outer = dyn Inner + 'r>) { /*check*/ g1(x) } +// FIXME: Ideally, we'd elaborate `dyn Inner` to `dyn Inner + 'r` here instead of rejecting it. +fn g1<'r>(_: impl Outer = dyn Inner>) {} +//~^ ERROR cannot deduce the lifetime bound for this trait object type from context + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.stderr b/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.stderr new file mode 100644 index 0000000000000..2ef05321862a9 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-binding-item-bounds.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-gat-binding-item-bounds.rs:17:33 + | +LL | fn f0<'r>(x: impl Outer = dyn Inner>) { /*check*/ g0(x) } + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn f0<'r>(x: impl Outer = dyn Inner + /* 'a */>) { /*check*/ g0(x) } + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-gat-binding-item-bounds.rs:23:33 + | +LL | fn g1<'r>(_: impl Outer = dyn Inner>) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g1<'r>(_: impl Outer = dyn Inner + /* 'a */>) {} + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-binding.rs b/tests/ui/object-lifetime/object-lifetime-default-gat-binding.rs new file mode 100644 index 0000000000000..ecc4bdc17fd82 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-binding.rs @@ -0,0 +1,34 @@ +// Ideally, GAT args in bindings would induce trait object lifetime defaults. +// +// However, since the current implementation can't handle this we instead conservatively and hackily +// treat the trait object lifetime default as indeterminate if any lifetime arguments are passed +// to the trait ref (or the GAT) thus rejecting any implicit trait object lifetime bounds. +// This way, we can still implement the desired behavior in the future. + +mod own { // the object lifetime default comes from the own generics + trait Outer { + type Ty<'a, T: ?Sized + 'a>; + } + + trait Inner {} + + fn f<'r>(x: impl Outer = ()>) { /*check*/ g(x) } + // FIXME: Ideally, we'd elaborate `dyn Inner` to `dyn Inner + 'r` instead of rejecting it. + fn g<'r>(_: impl Outer = ()>) {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +mod parent { // the object lifetime default comes from the parent generics + trait Outer<'a> { + type Ty; + } + + trait Inner {} + + fn f<'r>(x: impl Outer<'r, Ty = ()>) { /*check*/ g(x) } + // FIXME: Ideally, we'd elaborate `dyn Inner` to `dyn Inner + 'r` instead of rejecting it. + fn g<'r>(_: impl Outer<'r, Ty = ()>) {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-gat-binding.stderr b/tests/ui/object-lifetime/object-lifetime-default-gat-binding.stderr new file mode 100644 index 0000000000000..83809d9abc9fd --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-gat-binding.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-gat-binding.rs:17:35 + | +LL | fn g<'r>(_: impl Outer = ()>) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g<'r>(_: impl Outer = ()>) {} + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-gat-binding.rs:30:35 + | +LL | fn g<'r>(_: impl Outer<'r, Ty = ()>) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g<'r>(_: impl Outer<'r, Ty = ()>) {} + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-inferred-args.rs b/tests/ui/object-lifetime/object-lifetime-default-inferred-args.rs new file mode 100644 index 0000000000000..a191ac32a9e35 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inferred-args.rs @@ -0,0 +1,29 @@ +// Ensure that trait object lifetime defaulting properly deals with inferred args `_`. +// RBV which computes the defaults can't tell whether `_` refers to a type or const. +// Therefore it needs to map const params to a dummy trait object lifetime default +// in order to properly align params and args. +//@ check-pass + +struct Ty0<'a, Bait: 'static, T: 'a + ?Sized>(Bait, &'a T); + +fn check0<'r>(x: Ty0<'r, (), dyn Inner>) { + // The algorithm can't just avoid mapping const params to defaults and skip const & inferred + // args when mapping defaults to type args since the inferred arg may be a type. + // If it did, it would wrongly obtain the default provided by param `Bait` and elaborate + // `dyn Trait` to `dyn Trait + 'static` (which would fail since `'r` doesn't outlive `'static`). + let _: Ty0<'r, _, dyn Inner> = x; +} + +struct Ty1<'a, const BAIT: usize, T: 'a + ?Sized, Bait: 'static>(&'a T, Bait); + +fn check1<'r>(x: Ty1<'r, 0, dyn Inner, ()>) { + // The algorithm can't just avoid mapping const params to defaults, skip const args and count + // inferred args as types when mapping defaults to type args since the inferred arg may be a + // const. If it did, it would wrongly obtain the default provided by param `Bait` and elaborate + // `dyn Trait` to `dyn Trait + 'static` (which would fail since `'r` doesn't outlive `'static`). + let _: Ty1<'r, _, dyn Inner, _> = x; +} + +trait Inner {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-inferred.rs b/tests/ui/object-lifetime/object-lifetime-default-inferred.rs deleted file mode 100644 index d3162231faf77..0000000000000 --- a/tests/ui/object-lifetime/object-lifetime-default-inferred.rs +++ /dev/null @@ -1,33 +0,0 @@ -//@ run-pass -// Test that even with prior inferred parameters, object lifetimes of objects after are still -// valid. - - -#![allow(dead_code, unused)] - -trait Test { - fn foo(&self) { } -} - -struct Foo; -impl Test for Foo {} - -struct SomeStruct<'a> { - t: &'a dyn Test, - u: &'a (dyn Test+'a), -} - -fn a<'a, const N: usize>(_: [u8; N], t: &'a (dyn Test+'a), mut ss: SomeStruct<'a>) { - ss.t = t; -} - -fn b<'a, T>(_: T, t: &'a (dyn Test+'a), mut ss: SomeStruct<'a>) { - ss.u = t; -} - -fn main() { - // Inside a function body, we can just infer both - // lifetimes, to allow &'tmp (Display+'static). - a::<_>([], &Foo as &dyn Test, SomeStruct{t:&Foo,u:&Foo}); - b::<_>(0u8, &Foo as &dyn Test, SomeStruct{t:&Foo,u:&Foo}); -} diff --git a/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.rs b/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.rs new file mode 100644 index 0000000000000..6c6ba8002d45c --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.rs @@ -0,0 +1,43 @@ +// Exercise implicit trait object lifetime bounds inside +// type-level inherent (generic) associated const paths. +// See also . + +// FIXME: Ideally, this test would be check-pass. See below for details. + +#![feature(min_generic_const_args, inherent_associated_types, generic_const_items)] +#![expect(incomplete_features)] + +mod own { // the lifetime comes from the own generics + struct Parent; + impl Parent { + type const CT<'a, T: 'a + super::AbideBy<'a> + ?Sized>: usize = 0; + } + + // FIXME: Ideally, we would deduce `dyn Trait + 'r` from the bound `'a` on ty param `T` of + // type-level inherent assoc const `CT` but for that we'd need to somehow obtain the + // resolution of the type-relative path `Parent::CT` from HIR ty lowering in RBV. + fn check<'r>() where [(); Parent::CT::<'r, dyn super::Trait>]: {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +mod parent { // the lifetime comes from the parent generics + struct Parent<'a>(&'a ()); + impl<'a> Parent<'a> { + type const CT + ?Sized>: usize = 0; + } + + //FIXME: Ideally, we would deduce `dyn Trait + 'r` from the bound `'a` on ty param `T` of + // type-level inherent assoc const `CT` but for that we'd need to somehow obtain the + // resolution of the type-relative path `Parent::<'r>::CT` from HIR ty lowering in RBV. + fn check<'r>() where [(); Parent::<'r>::CT::]: {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +trait Trait {} + +// We use this to ensure that a given trait object lifetime bound is +// *exactly equal* to a given lifetime (not longer, not shorter). +trait AbideBy<'a> {} +impl<'a> AbideBy<'a> for dyn Trait + 'a {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.stderr b/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.stderr new file mode 100644 index 0000000000000..8323c2a94ce3a --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inherent-gac.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-inherent-gac.rs:19:48 + | +LL | fn check<'r>() where [(); Parent::CT::<'r, dyn super::Trait>]: {} + | ^^^^^^^^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn check<'r>() where [(); Parent::CT::<'r, dyn super::Trait + /* 'a */>]: {} + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-inherent-gac.rs:32:50 + | +LL | fn check<'r>() where [(); Parent::<'r>::CT::]: {} + | ^^^^^^^^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn check<'r>() where [(); Parent::<'r>::CT::]: {} + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.rs b/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.rs new file mode 100644 index 0000000000000..4469dc84d3eb7 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.rs @@ -0,0 +1,43 @@ +// Exercise implicit trait object lifetime bounds inside +// inherent (generic) associated type paths. +// See also: + +// FIXME: Ideally, this test would be check-pass. See below for details. + +#![feature(inherent_associated_types)] +#![expect(incomplete_features)] + +mod own { // the lifetime comes from the own generics + struct Parent; + impl Parent { + type Ty<'a, T: 'a + super::AbideBy<'a> + ?Sized> = (); + } + + // FIXME: Ideally, we would deduce `dyn Trait + 'r` from the bound `'a` on ty param `T` of + // inherent assoc ty `Ty` but for that we'd need to somehow obtain the resolution of the + // type-relative path `Parent::Ty` from HIR ty lowering in RBV. + fn check<'r>() where Parent::Ty<'r, dyn super::Trait>: {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +mod parent { // the lifetime comes from the parent generics + struct Parent<'a>(&'a ()); + impl<'a> Parent<'a> { + type Ty + ?Sized> = (); + } + + // FIXME: Ideally, we would deduce `dyn Trait + 'r` from the bound `'a` on ty param `T` of + // inherent assoc ty `Ty` but for that we'd need to somehow obtain the resolution of the + // type-relative path `Parent<'r>::Ty` from HIR ty lowering in RBV. + fn check<'r>() where Parent<'r>::Ty: {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +trait Trait {} + +// We use this to ensure that a given trait object lifetime bound is +// *exactly equal* to a given lifetime (not longer, not shorter). +trait AbideBy<'a> {} +impl<'a> AbideBy<'a> for dyn Trait + 'a {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.stderr b/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.stderr new file mode 100644 index 0000000000000..c41eafe19abee --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-inherent-gat.stderr @@ -0,0 +1,25 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-inherent-gat.rs:19:41 + | +LL | fn check<'r>() where Parent::Ty<'r, dyn super::Trait>: {} + | ^^^^^^^^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn check<'r>() where Parent::Ty<'r, dyn super::Trait + /* 'a */>: {} + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-inherent-gat.rs:32:41 + | +LL | fn check<'r>() where Parent<'r>::Ty: {} + | ^^^^^^^^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn check<'r>() where Parent<'r>::Ty: {} + | ++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-resolved-gat.rs b/tests/ui/object-lifetime/object-lifetime-default-resolved-gat.rs new file mode 100644 index 0000000000000..14e3aab9612b4 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-resolved-gat.rs @@ -0,0 +1,67 @@ +// Check that resolved GAT paths correctly induce trait object lifetime defaults. +// issue: + +//@ check-pass + +mod own { // the trait object lifetime default comes from the own generics + trait Outer { + type Ty<'a, T: 'a + super::AbideBy<'a> + ?Sized>; + } + impl Outer for () { + type Ty<'a, T: 'a + super::AbideBy<'a> + ?Sized> = (); + } + trait Inner {} + + // We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`. + fn check<'r>(_: <() as Outer>::Ty<'r, dyn super::Inner>) {} +} + +mod parent { // the trait object lifetime default comes from the parent generics + trait Outer<'a> { + type Ty + ?Sized>; + } + impl<'a> Outer<'a> for () { + type Ty + ?Sized> = (); + } + + // We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty `Ty`. + fn check<'r>(_: <() as Outer<'r>>::Ty) {} +} + +#[rustfmt::skip] +mod complex { + // We need to perform several delicate index calculations to map between the middle::ty and + // the HIR representation of generic args. This is a smoke test. + + trait Outer<'_pad0, 'a, '_pad1, _Pad0> { + type Ty< + '_pad2, 'b, '_pad3, _Pad1, + T: 'a + super::AbideBy<'a> + ?Sized, + _Pad2, + U: 'b + super::AbideBy<'b> + ?Sized, + >; + } + impl<'a, _Pad0> Outer<'_, 'a, '_, _Pad0> for () { + type Ty< + '_pad2, 'b, '_pad3, _Pad1, + T: 'a + super::AbideBy<'a> + ?Sized, + _Pad2, + U: 'b + super::AbideBy<'b> + ?Sized, + > = (); + } + + // We elaborate the 1st `dyn Inner` to `dyn Inner + 'r` and the 2nd one to `dyn Inner + 's`. + fn g<'r, 's>( + _: <() as Outer<'static, 'r, 'static, ()>> + ::Ty<'static, 's, 'static, (), dyn super::Inner, (), dyn super::Inner>, + ) {} +} + +trait Inner {} + +// We use this to ensure that a given trait object lifetime bound is +// *exactly equal* to a given lifetime (not longer, not shorter). +trait AbideBy<'a> {} +impl<'a> AbideBy<'a> for dyn Inner + 'a {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-trait-alias-ref.rs b/tests/ui/object-lifetime/object-lifetime-default-trait-alias-ref.rs new file mode 100644 index 0000000000000..d6efa32791b72 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-trait-alias-ref.rs @@ -0,0 +1,27 @@ +// Check that trait alias refs correctly induce trait object lifetime defaults. +// issue: +#![feature(trait_alias)] + +//@ check-pass + +trait Outer<'a, T: 'a + ?Sized> = Carry; +trait Carry {} + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn bound<'r, T>() where T: Outer<'r, dyn Inner> {} +fn check_bound<'r, T>() where T: Outer<'r, dyn Inner + 'r> { bound::<'r, T>();} + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn dyn_trait<'r>(_: Box>) {} +fn check_dyn_trait<'r>(x: Box>) { dyn_trait(x) } + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn impl_trait<'r>(_: impl Outer<'r, dyn Inner>) {} +fn check_impl_trait<'r>(x: impl Outer<'r, dyn Inner + 'r>) { impl_trait(x) } + +// Reasonably, trait aliases can't be used as the qself in fully qualified paths, +// so we don't need to test them here. + +trait Inner {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-trait-ref-lifetime-bound-on-assoc-ty.rs b/tests/ui/object-lifetime/object-lifetime-default-trait-ref-lifetime-bound-on-assoc-ty.rs new file mode 100644 index 0000000000000..ede0dc8832f0b --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-trait-ref-lifetime-bound-on-assoc-ty.rs @@ -0,0 +1,27 @@ +// Check that "children (assoc items) can't influence the parent (trait) wrt. obj lt defs". +// More concretely, check that lifetime bounds on assoc item defs don't influence the trait object +// lifetime defaults that are induced by the relevant trait ref +// So "information doesn't flow backwards". + +//@ check-pass + +trait Trait { type Assoc<'a> where T: 'a; } +impl Trait for () { type Assoc<'a> = &'a T where T: 'a; } + +trait Bound {} + +// We deduce `dyn Bound + 'static`. +fn f0(x: impl Trait) { /*check*/ g0(x) } +fn g0(_: impl Trait) {} + +// We deduce `dyn Bound + 'static` (and not `dyn Bound + 'r`). +// We intentionally don't make use of the `T: 'a` / `dyn Trait: 'r` bound +// that has to hold for the associated type binding to be valid. +fn f1<'r>(x: impl Trait = ()>) { /*check*/ g1(x) } +fn g1(_: impl Trait) {} + +// We deduce `dyn Bound + 'static` (and not `dyn Bound + 'r`). +fn f2<'r>(x: <() as Trait>::Assoc<'r>) { /*check*/ g2(x) } +fn g2<'r>(_: <() as Trait>::Assoc<'r>) {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs b/tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs new file mode 100644 index 0000000000000..1bed7369baa96 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-trait-ref.rs @@ -0,0 +1,34 @@ +// Check that trait refs correctly induce trait object lifetime defaults. +// +// For the longest time, all of the cases below used to get +// rejected as "inderminate" due to an off-by-one error. + +//@ check-pass + +trait Outer<'a, T: 'a + AbideBy<'a> + ?Sized> { type Ty where Self: Sized; } + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn bound<'r, T>() where T: Outer<'r, dyn Inner> {} + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn dyn_trait<'r>(_: Box>) {} +// Wfck doesn't require the type arguments in dyn trait to meet the bounds declared in the trait. +// Thus we can't rely on `AbideBy` here. Instead, just check if `'r` outlives the implicit bound. +fn check_dyn_trait<'r>(x: Box>) { dyn_trait(x) } + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn impl_trait<'r>(_: impl Outer<'r, dyn Inner>) {} + +// We deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of trait `Outer`. +fn assoc_ty_proj<'r>(_: <() as Outer<'r, dyn Inner>>::Ty) {} + +impl<'a, T: 'a + AbideBy<'a> + ?Sized> Outer<'a, T> for () { type Ty = &'a T; } + +trait Inner {} + +// We use this to ensure that a given trait object lifetime bound is +// *exactly equal* to a given lifetime (not longer, not shorter). +trait AbideBy<'a> {} +impl<'a> AbideBy<'a> for dyn Inner + 'a {} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.rs b/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.rs new file mode 100644 index 0000000000000..6e75610847d08 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.rs @@ -0,0 +1,32 @@ +// Exercise implicit trait object lifetime bounds inside type-relative assoc ty paths. + +mod own { // the lifetime comes from the own generics + trait Outer { type Ty<'a, T: 'a + ?Sized>; } + trait Inner {} + + fn f<'r, T: Outer>(x: T::Ty<'r, dyn Inner + 'r>) { /*check*/ g::(x) } + // FIXME: Ideally, we would deduce `dyn Inner + 'r` from bound `'a` on ty param `T` of assoc ty + // `Ty` but for that we'd need to somehow obtain the resolution of the type-relative path + // `T::Ty` from HIR ty lowering in RBV (it resolves to `::Ty`). + fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context +} + +mod parent { // the lifetime comes from the parent generics + trait Outer<'a> { type Ty; } + trait Inner {} + + fn f<'r, T: Outer<'r>>(x: T::Ty) { /*check*/ g::(x) } + // FIXME: Ideally, we would deduce `dyn Inner + 'r` from the bound `'a` on ty param `T` of assoc + // ty `Ty` but for that we'd need to somehow obtain the resolution of the type-relative + // path `T::Ty` from HIR ty lowering in RBV (it resolved to `>::Ty`). + fn g<'r, T: Outer<'r>>(_: T::Ty) {} + //~^ ERROR cannot deduce the lifetime bound for this trait object type from context + + // Assuming RBV was able to resolve type-relative paths, it should not crash on "overly lifetime + // polymorphic" paths (`for<'r> >::Ty` isn't a valid Rust type). + fn escaping Outer<'r>>(_: T::Ty) {} + //~^ ERROR cannot use the associated type of a trait with uninferred generic parameters +} + +fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.stderr b/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.stderr new file mode 100644 index 0000000000000..01f74e056f206 --- /dev/null +++ b/tests/ui/object-lifetime/object-lifetime-default-type-relative-gat.stderr @@ -0,0 +1,38 @@ +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-type-relative-gat.rs:11:37 + | +LL | fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner>) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g<'r, T: Outer>(x: T::Ty<'r, dyn Inner + /* 'a */>) {} + | ++++++++++ + +error[E0228]: cannot deduce the lifetime bound for this trait object type from context + --> $DIR/object-lifetime-default-type-relative-gat.rs:23:37 + | +LL | fn g<'r, T: Outer<'r>>(_: T::Ty) {} + | ^^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn g<'r, T: Outer<'r>>(_: T::Ty) {} + | ++++++++++ + +error[E0212]: cannot use the associated type of a trait with uninferred generic parameters + --> $DIR/object-lifetime-default-type-relative-gat.rs:28:42 + | +LL | fn escaping Outer<'r>>(_: T::Ty) {} + | ^^^^^^^^^^^^^^^^ + | +help: use a fully qualified path with inferred lifetimes + | +LL - fn escaping Outer<'r>>(_: T::Ty) {} +LL + fn escaping Outer<'r>>(_: >::Ty) {} + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0212, E0228. +For more information about an error, try `rustc --explain E0212`. diff --git a/tests/ui/object-lifetime/object-lifetime-default.rs b/tests/ui/object-lifetime/object-lifetime-default.rs index 74f5bb7ddb0ec..034928cbd5165 100644 --- a/tests/ui/object-lifetime/object-lifetime-default.rs +++ b/tests/ui/object-lifetime/object-lifetime-default.rs @@ -2,13 +2,13 @@ #[rustc_object_lifetime_default] struct A< - T, //~ ERROR BaseDefault + T, //~ ERROR Empty >(T); #[rustc_object_lifetime_default] struct B< 'a, - T, //~ ERROR BaseDefault + T, //~ ERROR Empty >(&'a (), T); #[rustc_object_lifetime_default] @@ -47,4 +47,12 @@ struct G< U: 'a + 'b, //~ ERROR Ambiguous >(&'a T, &'b U); +// Check that we also dump the default for the implicit `Self` type param of traits. +#[rustc_object_lifetime_default] +trait H< //~ ERROR 'a + 'a, + 'b, + T: 'b, //~ ERROR 'b +>: 'a {} + fn main() {} diff --git a/tests/ui/object-lifetime/object-lifetime-default.stderr b/tests/ui/object-lifetime/object-lifetime-default.stderr index a58afad3ef2be..6606676c69f72 100644 --- a/tests/ui/object-lifetime/object-lifetime-default.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default.stderr @@ -1,10 +1,10 @@ -error: BaseDefault +error: Empty --> $DIR/object-lifetime-default.rs:5:5 | LL | T, | ^ -error: BaseDefault +error: Empty --> $DIR/object-lifetime-default.rs:11:5 | LL | T, @@ -52,5 +52,21 @@ error: Ambiguous LL | U: 'a + 'b, | ^ -error: aborting due to 9 previous errors +error: 'a + --> $DIR/object-lifetime-default.rs:52:1 + | +LL | / trait H< +LL | | 'a, +LL | | 'b, +LL | | T: 'b, +LL | | >: 'a {} + | |_____^ + +error: 'b + --> $DIR/object-lifetime-default.rs:55:5 + | +LL | T: 'b, + | ^ + +error: aborting due to 11 previous errors