1
Fork 0

Auto merge of #111161 - compiler-errors:rtn-super, r=cjgillot

Support return-type bounds on associated methods from supertraits

Support `T: Trait<method(): Bound>` when `method` comes from a supertrait, aligning it with the behavior of associated type bounds (both equality and trait bounds).

The only wrinkle is that I have to extend `super_predicates_that_define_assoc_type` to look for *all* items, not just `AssocKind::Ty`. This will also be needed to support `feature(associated_const_equality)` as well, which is subtly broken when it comes to supertraits, though this PR does not fix those yet. There's a slight chance there's a perf regression here, in which case I guess I could split it out into a separate query.
This commit is contained in:
bors 2023-05-07 11:18:22 +00:00
commit 0dddad0dc5
17 changed files with 210 additions and 50 deletions

View file

@ -1062,7 +1062,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
/// Convert the bounds in `ast_bounds` that refer to traits which define an associated type
/// named `assoc_name` into ty::Bounds. Ignore the rest.
pub(crate) fn compute_bounds_that_match_assoc_type(
pub(crate) fn compute_bounds_that_match_assoc_item(
&self,
param_ty: Ty<'tcx>,
ast_bounds: &[hir::GenericBound<'_>],
@ -1073,7 +1073,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
for ast_bound in ast_bounds {
if let Some(trait_ref) = ast_bound.trait_ref()
&& let Some(trait_did) = trait_ref.trait_def_id()
&& self.tcx().trait_may_define_assoc_type(trait_did, assoc_name)
&& self.tcx().trait_may_define_assoc_item(trait_did, assoc_name)
{
result.push(ast_bound.clone());
}
@ -1141,11 +1141,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
) {
trait_ref
} else {
return Err(tcx.sess.emit_err(crate::errors::ReturnTypeNotationMissingMethod {
span: binding.span,
trait_name: tcx.item_name(trait_ref.def_id()),
assoc_name: binding.item_name.name,
}));
self.one_bound_for_assoc_method(
traits::supertraits(tcx, trait_ref),
trait_ref.print_only_trait_path(),
binding.item_name,
path_span,
)?
}
} else if self.trait_defines_associated_item_named(
trait_ref.def_id(),
@ -1946,7 +1947,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let param_name = tcx.hir().ty_param_name(ty_param_def_id);
self.one_bound_for_assoc_type(
|| {
traits::transitive_bounds_that_define_assoc_type(
traits::transitive_bounds_that_define_assoc_item(
tcx,
predicates.iter().filter_map(|(p, _)| {
Some(p.to_opt_poly_trait_pred()?.map_bound(|t| t.trait_ref))
@ -2081,6 +2082,46 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Ok(bound)
}
#[instrument(level = "debug", skip(self, all_candidates, ty_name), ret)]
fn one_bound_for_assoc_method(
&self,
all_candidates: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
ty_name: impl Display,
assoc_name: Ident,
span: Span,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
let mut matching_candidates = all_candidates.filter(|r| {
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Fn, assoc_name)
});
let candidate = match matching_candidates.next() {
Some(candidate) => candidate,
None => {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationMissingMethod {
span,
ty_name: ty_name.to_string(),
assoc_name: assoc_name.name,
},
));
}
};
if let Some(conflicting_candidate) = matching_candidates.next() {
return Err(self.tcx().sess.emit_err(
crate::errors::ReturnTypeNotationConflictingBound {
span,
ty_name: ty_name.to_string(),
assoc_name: assoc_name.name,
first_bound: candidate.print_only_trait_path(),
second_bound: conflicting_candidate.print_only_trait_path(),
},
));
}
Ok(candidate)
}
// Create a type from a path to an associated type or to an enum variant.
// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
// and item_segment is the path segment for `D`. We return a type and a def for

View file

@ -64,8 +64,8 @@ pub fn provide(providers: &mut Providers) {
explicit_predicates_of: predicates_of::explicit_predicates_of,
super_predicates_of: predicates_of::super_predicates_of,
implied_predicates_of: predicates_of::implied_predicates_of,
super_predicates_that_define_assoc_type:
predicates_of::super_predicates_that_define_assoc_type,
super_predicates_that_define_assoc_item:
predicates_of::super_predicates_that_define_assoc_item,
trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds,
type_param_predicates: predicates_of::type_param_predicates,
trait_def,

View file

@ -565,7 +565,7 @@ pub(super) fn super_predicates_of(
implied_predicates_with_filter(tcx, trait_def_id.to_def_id(), PredicateFilter::SelfOnly)
}
pub(super) fn super_predicates_that_define_assoc_type(
pub(super) fn super_predicates_that_define_assoc_item(
tcx: TyCtxt<'_>,
(trait_def_id, assoc_name): (DefId, Ident),
) -> ty::GenericPredicates<'_> {
@ -640,7 +640,7 @@ pub(super) fn implied_predicates_with_filter(
),
PredicateFilter::SelfThatDefines(assoc_name) => (
// Convert the bounds that follow the colon (or equal) that reference the associated name
icx.astconv().compute_bounds_that_match_assoc_type(self_param_ty, bounds, assoc_name),
icx.astconv().compute_bounds_that_match_assoc_item(self_param_ty, bounds, assoc_name),
// Include where clause bounds for `Self` that reference the associated name
icx.type_parameter_bounds_in_generics(
generics,
@ -819,7 +819,7 @@ impl<'tcx> ItemCtxt<'tcx> {
hir::GenericBound::Trait(poly_trait_ref, _) => {
let trait_ref = &poly_trait_ref.trait_ref;
if let Some(trait_did) = trait_ref.trait_def_id() {
self.tcx.trait_may_define_assoc_type(trait_did, assoc_name)
self.tcx.trait_may_define_assoc_item(trait_did, assoc_name)
} else {
false
}

View file

@ -1652,17 +1652,16 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
let bound_vars = if let Some(type_def_id) = type_def_id
&& self.tcx.def_kind(type_def_id) == DefKind::Trait
// FIXME(return_type_notation): We could bound supertrait methods.
&& let Some(assoc_fn) = self
.tcx
.associated_items(type_def_id)
.find_by_name_and_kind(self.tcx, binding.ident, ty::AssocKind::Fn, type_def_id)
&& let Some((mut bound_vars, assoc_fn)) =
BoundVarContext::supertrait_hrtb_vars(
self.tcx,
type_def_id,
binding.ident,
ty::AssocKind::Fn,
)
{
self.tcx
.generics_of(assoc_fn.def_id)
.params
.iter()
.map(|param| match param.kind {
bound_vars.extend(self.tcx.generics_of(assoc_fn.def_id).params.iter().map(
|param| match param.kind {
ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
ty::BoundRegionKind::BrNamed(param.def_id, param.name),
),
@ -1670,9 +1669,11 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
ty::BoundTyKind::Param(param.def_id, param.name),
),
ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
})
.chain(self.tcx.fn_sig(assoc_fn.def_id).subst_identity().bound_vars())
.collect()
},
));
bound_vars
.extend(self.tcx.fn_sig(assoc_fn.def_id).subst_identity().bound_vars());
bound_vars
} else {
self.tcx.sess.delay_span_bug(
binding.ident.span,
@ -1689,8 +1690,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
});
});
} else if let Some(type_def_id) = type_def_id {
let bound_vars =
BoundVarContext::supertrait_hrtb_vars(self.tcx, type_def_id, binding.ident);
let bound_vars = BoundVarContext::supertrait_hrtb_vars(
self.tcx,
type_def_id,
binding.ident,
ty::AssocKind::Type,
)
.map(|(bound_vars, _)| bound_vars);
self.with(scope, |this| {
let scope = Scope::Supertrait {
bound_vars: bound_vars.unwrap_or_default(),
@ -1720,11 +1726,15 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
def_id: DefId,
assoc_name: Ident,
) -> Option<Vec<ty::BoundVariableKind>> {
let trait_defines_associated_type_named = |trait_def_id: DefId| {
tcx.associated_items(trait_def_id)
.find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, trait_def_id)
.is_some()
assoc_kind: ty::AssocKind,
) -> Option<(Vec<ty::BoundVariableKind>, &'tcx ty::AssocItem)> {
let trait_defines_associated_item_named = |trait_def_id: DefId| {
tcx.associated_items(trait_def_id).find_by_name_and_kind(
tcx,
assoc_name,
assoc_kind,
trait_def_id,
)
};
use smallvec::{smallvec, SmallVec};
@ -1742,10 +1752,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
_ => break None,
}
if trait_defines_associated_type_named(def_id) {
break Some(bound_vars.into_iter().collect());
if let Some(assoc_item) = trait_defines_associated_item_named(def_id) {
break Some((bound_vars.into_iter().collect(), assoc_item));
}
let predicates = tcx.super_predicates_that_define_assoc_type((def_id, assoc_name));
let predicates = tcx.super_predicates_that_define_assoc_item((def_id, assoc_name));
let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| {
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {

View file

@ -6,7 +6,7 @@ use rustc_errors::{
MultiSpan,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty};
use rustc_span::{symbol::Ident, Span, Symbol};
#[derive(Diagnostic)]
@ -512,10 +512,22 @@ pub(crate) struct ReturnTypeNotationEqualityBound {
pub(crate) struct ReturnTypeNotationMissingMethod {
#[primary_span]
pub span: Span,
pub trait_name: Symbol,
pub ty_name: String,
pub assoc_name: Symbol,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_return_type_notation_conflicting_bound)]
#[note]
pub(crate) struct ReturnTypeNotationConflictingBound<'tcx> {
#[primary_span]
pub span: Span,
pub ty_name: String,
pub assoc_name: Symbol,
pub first_bound: ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
pub second_bound: ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_placeholder_not_allowed_item_signatures, code = "E0121")]
pub(crate) struct PlaceholderNotAllowedItemSignatures {