blob: d7a827c649ddc5c3c257af0e69a5f94e11b59531 [file] [log] [blame]
use std::ops::ControlFlow;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_hir::{AmbigArg, PolyTraitRef};
use rustc_middle::bug;
use rustc_middle::ty::{
self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
TypeVisitor, Upcast,
};
use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
use rustc_trait_selection::traits;
use smallvec::SmallVec;
use tracing::{debug, instrument};
use super::errors::GenericsArgsErrExtend;
use crate::errors;
use crate::hir_ty_lowering::{
AssocItemQSelf, FeedConstTy, HirTyLowerer, PredicateFilter, RegionInferReason,
};
#[derive(Debug, Default)]
struct CollectedBound {
/// `Trait`
positive: bool,
/// `?Trait`
maybe: bool,
/// `!Trait`
negative: bool,
}
impl CollectedBound {
/// Returns `true` if any of `Trait`, `?Trait` or `!Trait` were encountered.
fn any(&self) -> bool {
self.positive || self.maybe || self.negative
}
}
#[derive(Debug)]
struct CollectedSizednessBounds {
// Collected `Sized` bounds
sized: CollectedBound,
// Collected `MetaSized` bounds
meta_sized: CollectedBound,
// Collected `PointeeSized` bounds
pointee_sized: CollectedBound,
}
impl CollectedSizednessBounds {
/// Returns `true` if any of `Trait`, `?Trait` or `!Trait` were encountered for `Sized`,
/// `MetaSized` or `PointeeSized`.
fn any(&self) -> bool {
self.sized.any() || self.meta_sized.any() || self.pointee_sized.any()
}
}
fn search_bounds_for<'tcx>(
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
mut f: impl FnMut(&'tcx PolyTraitRef<'tcx>),
) {
let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
for hir_bound in hir_bounds {
let hir::GenericBound::Trait(ptr) = hir_bound else {
continue;
};
f(ptr)
}
};
search_bounds(hir_bounds);
if let Some((self_ty, where_clause)) = self_ty_where_predicates {
for clause in where_clause {
if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind
&& pred.is_param_bound(self_ty.to_def_id())
{
search_bounds(pred.bounds);
}
}
}
}
fn collect_relaxed_bounds<'tcx>(
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
) -> SmallVec<[&'tcx PolyTraitRef<'tcx>; 1]> {
let mut relaxed_bounds: SmallVec<[_; 1]> = SmallVec::new();
search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| {
if matches!(ptr.modifiers.polarity, hir::BoundPolarity::Maybe(_)) {
relaxed_bounds.push(ptr);
}
});
relaxed_bounds
}
fn collect_bounds<'a, 'tcx>(
hir_bounds: &'a [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
target_did: DefId,
) -> CollectedBound {
let mut collect_into = CollectedBound::default();
search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| {
if !matches!(ptr.trait_ref.path.res, Res::Def(DefKind::Trait, did) if did == target_did) {
return;
}
match ptr.modifiers.polarity {
hir::BoundPolarity::Maybe(_) => collect_into.maybe = true,
hir::BoundPolarity::Negative(_) => collect_into.negative = true,
hir::BoundPolarity::Positive => collect_into.positive = true,
}
});
collect_into
}
fn collect_sizedness_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
span: Span,
) -> CollectedSizednessBounds {
let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
let sized = collect_bounds(hir_bounds, self_ty_where_predicates, sized_did);
let meta_sized_did = tcx.require_lang_item(hir::LangItem::MetaSized, span);
let meta_sized = collect_bounds(hir_bounds, self_ty_where_predicates, meta_sized_did);
let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span);
let pointee_sized = collect_bounds(hir_bounds, self_ty_where_predicates, pointee_sized_did);
CollectedSizednessBounds { sized, meta_sized, pointee_sized }
}
/// Add a trait bound for `did`.
fn add_trait_bound<'tcx>(
tcx: TyCtxt<'tcx>,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
self_ty: Ty<'tcx>,
did: DefId,
span: Span,
) {
let trait_ref = ty::TraitRef::new(tcx, did, [self_ty]);
// Preferable to put sizedness obligations first, since we report better errors for `Sized`
// ambiguity.
bounds.insert(0, (trait_ref.upcast(tcx), span));
}
impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// Adds sizedness bounds to a trait, trait alias, parameter, opaque type or associated type.
///
/// - On parameters, opaque type and associated types, add default `Sized` bound if no explicit
/// sizedness bounds are present.
/// - On traits and trait aliases, add default `MetaSized` supertrait if no explicit sizedness
/// bounds are present.
/// - On parameters, opaque type, associated types and trait aliases, add a `MetaSized` bound if
/// a `?Sized` bound is present.
pub(crate) fn add_sizedness_bounds(
&self,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
self_ty: Ty<'tcx>,
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
trait_did: Option<LocalDefId>,
span: Span,
) {
let tcx = self.tcx();
// Skip adding any default bounds if `#![rustc_no_implicit_bounds]`
if tcx.has_attr(CRATE_DEF_ID, sym::rustc_no_implicit_bounds) {
return;
}
let meta_sized_did = tcx.require_lang_item(hir::LangItem::MetaSized, span);
let pointee_sized_did = tcx.require_lang_item(hir::LangItem::PointeeSized, span);
// If adding sizedness bounds to a trait, then there are some relevant early exits
if let Some(trait_did) = trait_did {
let trait_did = trait_did.to_def_id();
// Never add a default supertrait to `PointeeSized`.
if trait_did == pointee_sized_did {
return;
}
// Don't add default sizedness supertraits to auto traits because it isn't possible to
// relax an automatically added supertrait on the defn itself.
if tcx.trait_is_auto(trait_did) {
return;
}
} else {
// Report invalid relaxed bounds.
// FIXME: Since we only call this validation function here in this function, we only
// fully validate relaxed bounds in contexts where we perform
// "sized elaboration". In most cases that doesn't matter because we *usually*
// reject such relaxed bounds outright during AST lowering.
// However, this can easily get out of sync! Ideally, we would perform this step
// where we are guaranteed to catch *all* bounds like in
// `Self::lower_poly_trait_ref`. List of concrete issues:
// FIXME(more_maybe_bounds): We don't call this for e.g., trait object tys or
// supertrait bounds!
// FIXME(trait_alias, #143122): We don't call it for the RHS. Arguably however,
// AST lowering should reject them outright.
// FIXME(associated_type_bounds): We don't call this for them. However, AST
// lowering should reject them outright (#135229).
let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates);
self.check_and_report_invalid_relaxed_bounds(bounds);
}
let collected = collect_sizedness_bounds(tcx, hir_bounds, self_ty_where_predicates, span);
if (collected.sized.maybe || collected.sized.negative)
&& !collected.sized.positive
&& !collected.meta_sized.any()
&& !collected.pointee_sized.any()
{
// `?Sized` is equivalent to `MetaSized` (but only add the bound if there aren't any
// other explicit ones) - this can happen for trait aliases as well as bounds.
add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
} else if !collected.any() {
if trait_did.is_some() {
// If there are no explicit sizedness bounds on a trait then add a default
// `MetaSized` supertrait.
add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
} else {
// If there are no explicit sizedness bounds on a parameter then add a default
// `Sized` bound.
let sized_did = tcx.require_lang_item(hir::LangItem::Sized, span);
add_trait_bound(tcx, bounds, self_ty, sized_did, span);
}
}
}
/// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds
/// or associated items.
///
/// To keep backward compatibility with existing code, `experimental_default_bounds` bounds
/// should be added everywhere, including super bounds. However this causes a huge performance
/// costs. For optimization purposes instead of adding default supertraits, bounds
/// are added to the associated items:
///
/// ```ignore(illustrative)
/// // Default bounds are generated in the following way:
/// trait Trait {
/// fn foo(&self) where Self: Leak {}
/// }
///
/// // instead of this:
/// trait Trait: Leak {
/// fn foo(&self) {}
/// }
/// ```
/// It is not always possible to do this because of backward compatibility:
///
/// ```ignore(illustrative)
/// pub trait Trait<Rhs = Self> {}
/// pub trait Trait1 : Trait {}
/// //~^ ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait`
/// ```
///
/// or:
///
/// ```ignore(illustrative)
/// trait Trait {
/// type Type where Self: Sized;
/// }
/// trait Trait2<T> : Trait<Type = T> {}
/// //~^ ERROR: `DefaultAutoTrait` required for `Trait2`, by implicit `Self: DefaultAutoTrait` in `Trait::Type`
/// ```
///
/// Therefore, `experimental_default_bounds` are still being added to supertraits if
/// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header.
fn requires_default_supertraits(
&self,
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
hir_generics: &'tcx hir::Generics<'tcx>,
) -> bool {
struct TraitInfoCollector;
impl<'tcx> hir::intravisit::Visitor<'tcx> for TraitInfoCollector {
type Result = ControlFlow<()>;
fn visit_assoc_item_constraint(
&mut self,
_constraint: &'tcx hir::AssocItemConstraint<'tcx>,
) -> Self::Result {
ControlFlow::Break(())
}
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
if matches!(
&t.kind,
hir::TyKind::Path(hir::QPath::Resolved(
_,
hir::Path { res: hir::def::Res::SelfTyParam { .. }, .. },
))
) {
return ControlFlow::Break(());
}
hir::intravisit::walk_ty(self, t)
}
}
let mut found = false;
for bound in hir_bounds {
found |= hir::intravisit::walk_param_bound(&mut TraitInfoCollector, bound).is_break();
}
found |= hir::intravisit::walk_generics(&mut TraitInfoCollector, hir_generics).is_break();
found
}
/// Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if
/// they are not added as super trait bounds to the trait itself. See
/// `requires_default_supertraits` for more information.
pub(crate) fn add_default_trait_item_bounds(
&self,
trait_item: &hir::TraitItem<'tcx>,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
) {
let tcx = self.tcx();
if !tcx.sess.opts.unstable_opts.experimental_default_bounds {
return;
}
let parent = tcx.local_parent(trait_item.hir_id().owner.def_id);
let hir::Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else {
unreachable!();
};
let (trait_generics, trait_bounds) = match parent_trait.kind {
hir::ItemKind::Trait(_, _, _, _, generics, supertraits, _) => (generics, supertraits),
hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits),
_ => unreachable!(),
};
if !self.requires_default_supertraits(trait_bounds, trait_generics) {
let self_ty_where_predicates = (parent, trait_item.generics.predicates);
self.add_default_traits(
bounds,
tcx.types.self_param,
&[],
Some(self_ty_where_predicates),
trait_item.span,
);
}
}
/// Lazily sets `experimental_default_bounds` to true on trait super bounds.
/// See `requires_default_supertraits` for more information.
pub(crate) fn add_default_super_traits(
&self,
trait_def_id: LocalDefId,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
hir_bounds: &'tcx [hir::GenericBound<'tcx>],
hir_generics: &'tcx hir::Generics<'tcx>,
span: Span,
) {
if !self.tcx().sess.opts.unstable_opts.experimental_default_bounds {
return;
}
assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias));
if self.requires_default_supertraits(hir_bounds, hir_generics) {
let self_ty_where_predicates = (trait_def_id, hir_generics.predicates);
self.add_default_traits(
bounds,
self.tcx().types.self_param,
hir_bounds,
Some(self_ty_where_predicates),
span,
);
}
}
pub(crate) fn add_default_traits(
&self,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
self_ty: Ty<'tcx>,
hir_bounds: &[hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
span: Span,
) {
self.tcx().default_traits().iter().for_each(|default_trait| {
self.add_default_trait(
*default_trait,
bounds,
self_ty,
hir_bounds,
self_ty_where_predicates,
span,
);
});
}
/// Add a `experimental_default_bounds` bound to the `bounds` if appropriate.
///
/// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`.
pub(crate) fn add_default_trait(
&self,
trait_: hir::LangItem,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
self_ty: Ty<'tcx>,
hir_bounds: &[hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
span: Span,
) {
let tcx = self.tcx();
let trait_id = tcx.lang_items().get(trait_);
if let Some(trait_id) = trait_id
&& self.should_add_default_traits(trait_id, hir_bounds, self_ty_where_predicates)
{
add_trait_bound(tcx, bounds, self_ty, trait_id, span);
}
}
/// Returns `true` if default trait bound should be added.
fn should_add_default_traits<'a>(
&self,
trait_def_id: DefId,
hir_bounds: &'a [hir::GenericBound<'tcx>],
self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
) -> bool {
let collected = collect_bounds(hir_bounds, self_ty_where_predicates, trait_def_id);
!self.tcx().has_attr(CRATE_DEF_ID, sym::rustc_no_implicit_bounds) && !collected.any()
}
/// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any.
///
/// ### Examples
///
/// ```ignore (illustrative)
/// fn foo<T>() where for<'a> T: Trait<'a> + Copy {}
/// // ^^^^^^^ ^ ^^^^^^^^^^^^^^^^ `hir_bounds`, in HIR form
/// // | |
/// // | `param_ty`, in ty form
/// // `bound_vars`, in ty form
///
/// fn bar<T>() where T: for<'a> Trait<'a> + Copy {} // no overarching `bound_vars` here!
/// // ^ ^^^^^^^^^^^^^^^^^^^^^^^^ `hir_bounds`, in HIR form
/// // |
/// // `param_ty`, in ty form
/// ```
///
/// ### A Note on Binders
///
/// There is an implied binder around `param_ty` and `hir_bounds`.
/// See `lower_poly_trait_ref` for more details.
#[instrument(level = "debug", skip(self, hir_bounds, bounds))]
pub(crate) fn lower_bounds<'hir, I: IntoIterator<Item = &'hir hir::GenericBound<'tcx>>>(
&self,
param_ty: Ty<'tcx>,
hir_bounds: I,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
predicate_filter: PredicateFilter,
) where
'tcx: 'hir,
{
for hir_bound in hir_bounds {
// In order to avoid cycles, when we're lowering `SelfTraitThatDefines`,
// we skip over any traits that don't define the given associated type.
if let PredicateFilter::SelfTraitThatDefines(assoc_ident) = predicate_filter {
if let Some(trait_ref) = hir_bound.trait_ref()
&& let Some(trait_did) = trait_ref.trait_def_id()
&& self.tcx().trait_may_define_assoc_item(trait_did, assoc_ident)
{
// Okay
} else {
continue;
}
}
match hir_bound {
hir::GenericBound::Trait(poly_trait_ref) => {
let _ = self.lower_poly_trait_ref(
poly_trait_ref,
param_ty,
bounds,
predicate_filter,
);
}
hir::GenericBound::Outlives(lifetime) => {
// `ConstIfConst` is only interested in `[const]` bounds.
if matches!(
predicate_filter,
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst
) {
continue;
}
let region = self.lower_lifetime(lifetime, RegionInferReason::OutlivesBound);
let bound = ty::Binder::bind_with_vars(
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(param_ty, region)),
bound_vars,
);
bounds.push((bound.upcast(self.tcx()), lifetime.ident.span));
}
hir::GenericBound::Use(..) => {
// We don't actually lower `use` into the type layer.
}
}
}
}
/// Lower an associated item constraint from the HIR into `bounds`.
///
/// ### A Note on Binders
///
/// Given something like `T: for<'a> Iterator<Item = &'a u32>`,
/// the `trait_ref` here will be `for<'a> T: Iterator`.
/// The `constraint` data however is from *inside* the binder
/// (e.g., `&'a u32`) and hence may reference bound regions.
#[instrument(level = "debug", skip(self, bounds, duplicates, path_span))]
pub(super) fn lower_assoc_item_constraint(
&self,
hir_ref_id: hir::HirId,
trait_ref: ty::PolyTraitRef<'tcx>,
constraint: &hir::AssocItemConstraint<'tcx>,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
duplicates: &mut FxIndexMap<DefId, Span>,
path_span: Span,
predicate_filter: PredicateFilter,
) -> Result<(), ErrorGuaranteed> {
let tcx = self.tcx();
let assoc_tag = if constraint.gen_args.parenthesized
== hir::GenericArgsParentheses::ReturnTypeNotation
{
ty::AssocTag::Fn
} else if let hir::AssocItemConstraintKind::Equality { term: hir::Term::Const(_) } =
constraint.kind
{
ty::AssocTag::Const
} else {
ty::AssocTag::Type
};
// Given something like `U: Trait<T = X>`, we want to produce a predicate like
// `<U as Trait>::T = X`.
// This is somewhat subtle in the event that `T` is defined in a supertrait of `Trait`,
// because in that case we need to upcast. I.e., we want to produce
// `<B as SuperTrait<i32>>::T == X` for `B: SubTrait<T = X>` where
//
// trait SubTrait: SuperTrait<i32> {}
// trait SuperTrait<A> { type T; }
let candidate = if self.probe_trait_that_defines_assoc_item(
trait_ref.def_id(),
assoc_tag,
constraint.ident,
) {
// Simple case: The assoc item is defined in the current trait.
trait_ref
} else {
// Otherwise, we have to walk through the supertraits to find
// one that does define it.
self.probe_single_bound_for_assoc_item(
|| traits::supertraits(tcx, trait_ref),
AssocItemQSelf::Trait(trait_ref.def_id()),
assoc_tag,
constraint.ident,
path_span,
Some(constraint),
)?
};
let assoc_item = self
.probe_assoc_item(
constraint.ident,
assoc_tag,
hir_ref_id,
constraint.span,
candidate.def_id(),
)
.expect("failed to find associated item");
duplicates
.entry(assoc_item.def_id)
.and_modify(|prev_span| {
self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
span: constraint.span,
prev_span: *prev_span,
item_name: constraint.ident,
def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
});
})
.or_insert(constraint.span);
let projection_term = if let ty::AssocTag::Fn = assoc_tag {
let bound_vars = tcx.late_bound_vars(constraint.hir_id);
ty::Binder::bind_with_vars(
self.lower_return_type_notation_ty(candidate, assoc_item.def_id, path_span)?.into(),
bound_vars,
)
} else {
// Create the generic arguments for the associated type or constant by joining the
// parent arguments (the arguments of the trait) and the own arguments (the ones of
// the associated item itself) and construct an alias type using them.
let alias_term = candidate.map_bound(|trait_ref| {
let item_segment = hir::PathSegment {
ident: constraint.ident,
hir_id: constraint.hir_id,
res: Res::Err,
args: Some(constraint.gen_args),
infer_args: false,
};
let alias_args = self.lower_generic_args_of_assoc_item(
path_span,
assoc_item.def_id,
&item_segment,
trait_ref.args,
);
debug!(?alias_args);
ty::AliasTerm::new_from_args(tcx, assoc_item.def_id, alias_args)
});
// Provide the resolved type of the associated constant to `type_of(AnonConst)`.
if let Some(const_arg) = constraint.ct() {
if let hir::ConstArgKind::Anon(anon_const) = const_arg.kind {
let ty = alias_term
.map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args));
let ty = check_assoc_const_binding_type(
self,
constraint.ident,
ty,
constraint.hir_id,
);
tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
}
}
alias_term
};
match constraint.kind {
hir::AssocItemConstraintKind::Equality { .. } if let ty::AssocTag::Fn = assoc_tag => {
return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound {
span: constraint.span,
}));
}
// Lower an equality constraint like `Item = u32` as found in HIR bound `T: Iterator<Item = u32>`
// to a projection predicate: `<T as Iterator>::Item = u32`.
hir::AssocItemConstraintKind::Equality { term } => {
let term = match term {
hir::Term::Ty(ty) => self.lower_ty(ty).into(),
hir::Term::Const(ct) => self.lower_const_arg(ct, FeedConstTy::No).into(),
};
// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref or assoc_item. These are not well-formed.
//
// Example:
//
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
let late_bound_in_projection_ty =
tcx.collect_constrained_late_bound_regions(projection_term);
let late_bound_in_term =
tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term));
debug!(?late_bound_in_projection_ty);
debug!(?late_bound_in_term);
// FIXME: point at the type params that don't have appropriate lifetimes:
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
// ---- ---- ^^^^^^^
// NOTE(associated_const_equality): This error should be impossible to trigger
// with associated const equality constraints.
self.validate_late_bound_regions(
late_bound_in_projection_ty,
late_bound_in_term,
|br_name| {
struct_span_code_err!(
self.dcx(),
constraint.span,
E0582,
"binding for associated type `{}` references {}, \
which does not appear in the trait input types",
constraint.ident,
br_name
)
},
);
match predicate_filter {
PredicateFilter::All
| PredicateFilter::SelfOnly
| PredicateFilter::SelfAndAssociatedTypeBounds => {
let bound = projection_term.map_bound(|projection_term| {
ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_term,
term,
})
});
bounds.push((bound.upcast(tcx), constraint.span));
}
// SelfTraitThatDefines is only interested in trait predicates.
PredicateFilter::SelfTraitThatDefines(_) => {}
// `ConstIfConst` is only interested in `[const]` bounds.
PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {}
}
}
// Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator<Item: Debug>`
// to a bound involving a projection: `<T as Iterator>::Item: Debug`.
hir::AssocItemConstraintKind::Bound { bounds: hir_bounds } => {
match predicate_filter {
PredicateFilter::All
| PredicateFilter::SelfAndAssociatedTypeBounds
| PredicateFilter::ConstIfConst => {
let projection_ty = projection_term
.map_bound(|projection_term| projection_term.expect_ty(self.tcx()));
// Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty`
// parameter to have a skipped binder.
let param_ty =
Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder());
self.lower_bounds(
param_ty,
hir_bounds,
bounds,
projection_ty.bound_vars(),
predicate_filter,
);
}
PredicateFilter::SelfOnly
| PredicateFilter::SelfTraitThatDefines(_)
| PredicateFilter::SelfConstIfConst => {}
}
}
}
Ok(())
}
/// Lower a type, possibly specially handling the type if it's a return type notation
/// which we otherwise deny in other positions.
pub fn lower_ty_maybe_return_type_notation(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
let hir::TyKind::Path(qpath) = hir_ty.kind else {
return self.lower_ty(hir_ty);
};
let tcx = self.tcx();
match qpath {
hir::QPath::Resolved(opt_self_ty, path)
if let [mod_segments @ .., trait_segment, item_segment] = &path.segments[..]
&& item_segment.args.is_some_and(|args| {
matches!(
args.parenthesized,
hir::GenericArgsParentheses::ReturnTypeNotation
)
}) =>
{
// We don't allow generics on the module segments.
let _ =
self.prohibit_generic_args(mod_segments.iter(), GenericsArgsErrExtend::None);
let item_def_id = match path.res {
Res::Def(DefKind::AssocFn, item_def_id) => item_def_id,
Res::Err => {
return Ty::new_error_with_message(
tcx,
hir_ty.span,
"failed to resolve RTN",
);
}
_ => bug!("only expected method resolution for fully qualified RTN"),
};
let trait_def_id = tcx.parent(item_def_id);
// Good error for `where Trait::method(..): Send`.
let Some(self_ty) = opt_self_ty else {
let guar = self.report_missing_self_ty_for_resolved_path(
trait_def_id,
hir_ty.span,
item_segment,
ty::AssocTag::Type,
);
return Ty::new_error(tcx, guar);
};
let self_ty = self.lower_ty(self_ty);
let trait_ref = self.lower_mono_trait_ref(
hir_ty.span,
trait_def_id,
self_ty,
trait_segment,
false,
);
// SUBTLE: As noted at the end of `try_append_return_type_notation_params`
// in `resolve_bound_vars`, we stash the explicit bound vars of the where
// clause onto the item segment of the RTN type. This allows us to know
// how many bound vars are *not* coming from the signature of the function
// from lowering RTN itself.
//
// For example, in `where for<'a> <T as Trait<'a>>::method(..): Other`,
// the `late_bound_vars` of the where clause predicate (i.e. this HIR ty's
// parent) will include `'a` AND all the early- and late-bound vars of the
// method. But when lowering the RTN type, we just want the list of vars
// we used to resolve the trait ref. We explicitly stored those back onto
// the item segment, since there's no other good place to put them.
let candidate =
ty::Binder::bind_with_vars(trait_ref, tcx.late_bound_vars(item_segment.hir_id));
match self.lower_return_type_notation_ty(candidate, item_def_id, hir_ty.span) {
Ok(ty) => Ty::new_alias(tcx, ty::Projection, ty),
Err(guar) => Ty::new_error(tcx, guar),
}
}
hir::QPath::TypeRelative(hir_self_ty, segment)
if segment.args.is_some_and(|args| {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
let self_ty = self.lower_ty(hir_self_ty);
let (item_def_id, bound) = match self.resolve_type_relative_path(
self_ty,
hir_self_ty,
ty::AssocTag::Fn,
segment,
hir_ty.hir_id,
hir_ty.span,
None,
) {
Ok(result) => result,
Err(guar) => return Ty::new_error(tcx, guar),
};
// Don't let `T::method` resolve to some `for<'a> <T as Tr<'a>>::method`,
// which may happen via a higher-ranked where clause or supertrait.
// This is the same restrictions as associated types; even though we could
// support it, it just makes things a lot more difficult to support in
// `resolve_bound_vars`, since we'd need to introduce those as elided
// bound vars on the where clause too.
if bound.has_bound_vars() {
return Ty::new_error(
tcx,
self.dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
span: hir_ty.span,
inferred_sugg: Some(hir_ty.span.with_hi(segment.ident.span.lo())),
bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder()),
mpart_sugg: None,
what: tcx.def_descr(item_def_id),
}),
);
}
match self.lower_return_type_notation_ty(bound, item_def_id, hir_ty.span) {
Ok(ty) => Ty::new_alias(tcx, ty::Projection, ty),
Err(guar) => Ty::new_error(tcx, guar),
}
}
_ => self.lower_ty(hir_ty),
}
}
/// Do the common parts of lowering an RTN type. This involves extending the
/// candidate binder to include all of the early- and late-bound vars that are
/// defined on the function itself, and constructing a projection to the RPITIT
/// return type of that function.
fn lower_return_type_notation_ty(
&self,
candidate: ty::PolyTraitRef<'tcx>,
item_def_id: DefId,
path_span: Span,
) -> Result<ty::AliasTy<'tcx>, ErrorGuaranteed> {
let tcx = self.tcx();
let mut emitted_bad_param_err = None;
// If we have an method return type bound, then we need to instantiate
// the method's early bound params with suitable late-bound params.
let mut num_bound_vars = candidate.bound_vars().len();
let args = candidate.skip_binder().args.extend_to(tcx, item_def_id, |param, _| {
let arg = match param.kind {
ty::GenericParamDefKind::Lifetime => ty::Region::new_bound(
tcx,
ty::INNERMOST,
ty::BoundRegion {
var: ty::BoundVar::from_usize(num_bound_vars),
kind: ty::BoundRegionKind::Named(param.def_id),
},
)
.into(),
ty::GenericParamDefKind::Type { .. } => {
let guar = *emitted_bad_param_err.get_or_insert_with(|| {
self.dcx().emit_err(crate::errors::ReturnTypeNotationIllegalParam::Type {
span: path_span,
param_span: tcx.def_span(param.def_id),
})
});
Ty::new_error(tcx, guar).into()
}
ty::GenericParamDefKind::Const { .. } => {
let guar = *emitted_bad_param_err.get_or_insert_with(|| {
self.dcx().emit_err(crate::errors::ReturnTypeNotationIllegalParam::Const {
span: path_span,
param_span: tcx.def_span(param.def_id),
})
});
ty::Const::new_error(tcx, guar).into()
}
};
num_bound_vars += 1;
arg
});
// Next, we need to check that the return-type notation is being used on
// an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait).
let output = tcx.fn_sig(item_def_id).skip_binder().output();
let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind()
&& tcx.is_impl_trait_in_trait(alias_ty.def_id)
{
alias_ty
} else {
return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
span: path_span,
ty: tcx.liberate_late_bound_regions(item_def_id, output),
fn_span: tcx.hir_span_if_local(item_def_id),
note: (),
}));
};
// Finally, move the fn return type's bound vars over to account for the early bound
// params (and trait ref's late bound params). This logic is very similar to
// `rustc_middle::ty::predicate::Clause::instantiate_supertrait`
// and it's no coincidence why.
let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output);
Ok(ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args))
}
}
/// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings.
///
/// FIXME(const_generics): This is a temporary and semi-artificial restriction until the
/// arrival of *generic const generics*[^1].
///
/// It might actually be possible that we can already support early-bound generic params
/// in such types if we just lifted some more checks in other places, too, for example
/// inside `HirTyLowerer::lower_anon_const`. However, even if that were the case, we should
/// probably gate this behind another feature flag.
///
/// [^1]: <https://github.com/rust-lang/project-const-generics/issues/28>.
fn check_assoc_const_binding_type<'tcx>(
cx: &dyn HirTyLowerer<'tcx>,
assoc_const: Ident,
ty: ty::Binder<'tcx, Ty<'tcx>>,
hir_id: hir::HirId,
) -> Ty<'tcx> {
// We can't perform the checks for early-bound params during name resolution unlike E0770
// because this information depends on *type* resolution.
// We can't perform these checks in `resolve_bound_vars` either for the same reason.
// Consider the trait ref `for<'a> Trait<'a, C = { &0 }>`. We need to know the fully
// resolved type of `Trait::C` in order to know if it references `'a` or not.
let ty = ty.skip_binder();
if !ty.has_param() && !ty.has_escaping_bound_vars() {
return ty;
}
let mut collector = GenericParamAndBoundVarCollector {
cx,
params: Default::default(),
vars: Default::default(),
depth: ty::INNERMOST,
};
let mut guar = ty.visit_with(&mut collector).break_value();
let tcx = cx.tcx();
let ty_note = ty
.make_suggestable(tcx, false, None)
.map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty });
let enclosing_item_owner_id = tcx
.hir_parent_owner_iter(hir_id)
.find_map(|(owner_id, parent)| parent.generics().map(|_| owner_id))
.unwrap();
let generics = tcx.generics_of(enclosing_item_owner_id);
for index in collector.params {
let param = generics.param_at(index as _, tcx);
let is_self_param = param.name == kw::SelfUpper;
guar.get_or_insert(cx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
span: assoc_const.span,
assoc_const,
param_name: param.name,
param_def_kind: tcx.def_descr(param.def_id),
param_category: if is_self_param {
"self"
} else if param.kind.is_synthetic() {
"synthetic"
} else {
"normal"
},
param_defined_here_label:
(!is_self_param).then(|| tcx.def_ident_span(param.def_id).unwrap()),
ty_note,
}));
}
for var_def_id in collector.vars {
guar.get_or_insert(cx.dcx().emit_err(
crate::errors::EscapingBoundVarInTyOfAssocConstBinding {
span: assoc_const.span,
assoc_const,
var_name: cx.tcx().item_name(var_def_id),
var_def_kind: tcx.def_descr(var_def_id),
var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(),
ty_note,
},
));
}
let guar = guar.unwrap_or_else(|| bug!("failed to find gen params or bound vars in ty"));
Ty::new_error(tcx, guar)
}
struct GenericParamAndBoundVarCollector<'a, 'tcx> {
cx: &'a dyn HirTyLowerer<'tcx>,
params: FxIndexSet<u32>,
vars: FxIndexSet<DefId>,
depth: ty::DebruijnIndex,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 'tcx> {
type Result = ControlFlow<ErrorGuaranteed>;
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
&mut self,
binder: &ty::Binder<'tcx, T>,
) -> Self::Result {
self.depth.shift_in(1);
let result = binder.super_visit_with(self);
self.depth.shift_out(1);
result
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
match ty.kind() {
ty::Param(param) => {
self.params.insert(param.index);
}
ty::Bound(db, bt) if *db >= self.depth => {
self.vars.insert(match bt.kind {
ty::BoundTyKind::Param(def_id) => def_id,
ty::BoundTyKind::Anon => {
let reported = self
.cx
.dcx()
.delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var));
return ControlFlow::Break(reported);
}
});
}
_ if ty.has_param() || ty.has_bound_vars() => return ty.super_visit_with(self),
_ => {}
}
ControlFlow::Continue(())
}
fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result {
match re.kind() {
ty::ReEarlyParam(param) => {
self.params.insert(param.index);
}
ty::ReBound(db, br) if db >= self.depth => {
self.vars.insert(match br.kind {
ty::BoundRegionKind::Named(def_id) => def_id,
ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => {
let guar = self
.cx
.dcx()
.delayed_bug(format!("unexpected bound region kind: {:?}", br.kind));
return ControlFlow::Break(guar);
}
ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
});
}
_ => {}
}
ControlFlow::Continue(())
}
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
match ct.kind() {
ty::ConstKind::Param(param) => {
self.params.insert(param.index);
}
ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => {
let guar = self.cx.dcx().delayed_bug("unexpected escaping late-bound const var");
return ControlFlow::Break(guar);
}
_ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self),
_ => {}
}
ControlFlow::Continue(())
}
}