Skip to content

Commit 7c8e281

Browse files
Flesh out some TODOs
1 parent 51b51bb commit 7c8e281

File tree

7 files changed

+97
-19
lines changed

7 files changed

+97
-19
lines changed

compiler/rustc_hir_analysis/messages.ftl

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
3737
3838
hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here
3939
40-
hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated type of a trait with uninferred generic parameters
40+
hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated {$what} of a trait with uninferred generic parameters
4141
.suggestion = use a fully qualified path with inferred lifetimes
4242
4343
hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes

compiler/rustc_hir_analysis/src/collect.rs

+1
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
507507
inferred_sugg,
508508
bound,
509509
mpart_sugg,
510+
what: "type",
510511
}),
511512
)
512513
}

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

+40-8
Original file line numberDiff line numberDiff line change
@@ -1845,19 +1845,38 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
18451845
assert_eq!(old_value, Some(bad_def));
18461846
}
18471847

1848-
// TODO:
1848+
// When we have a return type notation type in a where clause, like
1849+
// `where <T as Trait>::method(..): Send`, we need to introduce new bound
1850+
// vars to the existing where clause's binder, to represent the lifetimes
1851+
// elided by the return-type-notation syntax.
1852+
//
1853+
// For example, given
1854+
// ```
1855+
// trait Foo {
1856+
// async fn x<'r, T>();
1857+
// }
1858+
// ```
1859+
// and a bound that looks like:
1860+
// `for<'a, 'b> <T as Trait<'a>>::x(): Other<'b>`
1861+
// this is going to expand to something like:
1862+
// `for<'a, 'b, 'r, T> <T as Trait<'a>>::x::<'r, T>::{opaque#0}: Other<'b>`.
1863+
//
1864+
// We handle this similarly for associated-type-bound style return-type-notation
1865+
// in `visit_segment_args`.
18491866
fn try_append_return_type_notation_params(
18501867
&mut self,
18511868
hir_id: HirId,
18521869
hir_ty: &'tcx hir::Ty<'tcx>,
18531870
) {
18541871
let hir::TyKind::Path(qpath) = hir_ty.kind else {
1855-
// TODO:
1872+
// We only care about path types here. All other self types
1873+
// (including nesting the RTN type in another type) don't do
1874+
// anything.
18561875
return;
18571876
};
18581877

18591878
let (mut bound_vars, item_def_id, item_segment) = match qpath {
1860-
// TODO:
1879+
// If we have a fully qualified method, then we don't need to do any special lookup.
18611880
hir::QPath::Resolved(_, path)
18621881
if let [.., item_segment] = &path.segments[..]
18631882
&& item_segment.args.is_some_and(|args| {
@@ -1873,23 +1892,32 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
18731892
(vec![], item_def_id, item_segment)
18741893
}
18751894

1876-
// TODO:
1895+
// If we have a type-dependent path, then we do need to do some lookup.
18771896
hir::QPath::TypeRelative(qself, item_segment)
18781897
if item_segment.args.is_some_and(|args| {
18791898
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
18801899
}) =>
18811900
{
1901+
// First, ignore a qself that isn't a type or `Self` param. Those are the
1902+
// only ones that support `T::Assoc` anyways in HIR lowering.
18821903
let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind else {
18831904
return;
18841905
};
1885-
18861906
match path.res {
18871907
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => {
1908+
// Get the generics of this type's hir owner. This is *different*
1909+
// from the generics of the parameter's definition, since we want
1910+
// to be able to resolve an RTN path on a nested body (e.g. method
1911+
// inside an impl) using the where clauses on the method.
18881912
let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics()
18891913
else {
18901914
return;
18911915
};
18921916

1917+
// Look for the first bound that contains an associated type that
1918+
// matches the segment that we're looking for. We ignore any subsequent
1919+
// bounds since we'll be emitting a hard error in HIR lowering, so this
1920+
// is purely speculative.
18931921
let one_bound = generics.predicates.iter().find_map(|predicate| {
18941922
let hir::WherePredicate::BoundPredicate(predicate) = predicate else {
18951923
return None;
@@ -1927,7 +1955,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
19271955
_ => return,
19281956
};
19291957

1930-
// TODO:
1958+
// Append the early-bound vars on the function, and then the late-bound ones.
1959+
// We actually turn type parameters into higher-ranked types here, but we
1960+
// deny them later in HIR lowering.
19311961
bound_vars.extend(self.tcx.generics_of(item_def_id).own_params.iter().map(|param| {
19321962
match param.kind {
19331963
ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
@@ -1941,11 +1971,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
19411971
}));
19421972
bound_vars.extend(self.tcx.fn_sig(item_def_id).instantiate_identity().bound_vars());
19431973

1944-
// TODO:
1974+
// SUBTLE: Stash the old bound vars onto the *item segment* before appending
1975+
// the new bound vars. We do this because we need to know how many bound vars
1976+
// are present on the binder explicitly (i.e. not return-type-notation vars)
1977+
// to do bound var shifting correctly in HIR lowering.
19451978
let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id).unwrap();
19461979
let existing_bound_vars_saved = existing_bound_vars.clone();
19471980
existing_bound_vars.extend(bound_vars);
1948-
// TODO: subtle
19491981
self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved);
19501982
}
19511983
}

compiler/rustc_hir_analysis/src/errors.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,8 @@ pub(crate) struct AssociatedTypeTraitUninferredGenericParams {
787787
pub inferred_sugg: Option<Span>,
788788
pub bound: String,
789789
#[subdiagnostic]
790-
pub mpart_sugg: Option<AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion>,
790+
pub mpart_sugg: Option<AssociatedItemTraitUninferredGenericParamsMultipartSuggestion>,
791+
pub what: &'static str,
791792
}
792793

793794
#[derive(Subdiagnostic)]

compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
465465
Ok(())
466466
}
467467

468-
// TODO:
468+
/// Lower a type, possibly specially handling the type if it's a return type notation
469+
/// which we otherwise deny in other positions.
469470
pub fn lower_ty_maybe_return_type_notation(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
470471
let hir::TyKind::Path(qpath) = hir_ty.kind else {
471472
return self.lower_ty(hir_ty);
@@ -482,14 +483,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
482483
)
483484
}) =>
484485
{
486+
// We don't allow generics on the module segments.
485487
let _ =
486488
self.prohibit_generic_args(mod_segments.iter(), GenericsArgsErrExtend::None);
487489

488490
let Res::Def(DefKind::AssocFn, item_def_id) = path.res else {
489-
bug!();
491+
bug!("expected RTN to resolve to associated fn");
490492
};
491493
let trait_def_id = tcx.parent(item_def_id);
492494

495+
// Good error for `where Trait::method(..): Send`.
493496
let Some(self_ty) = opt_self_ty else {
494497
return self.error_missing_qpath_self_ty(
495498
trait_def_id,
@@ -508,6 +511,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
508511
ty::BoundConstness::NotConst,
509512
);
510513

514+
// SUBTLE: As noted at the end of `try_append_return_type_notation_params`
515+
// in `resolve_bound_vars`, we stash the explicit bound vars of the where
516+
// clause onto the item segment of the RTN type. This allows us to know
517+
// how many bound vars are *not* coming from the signature of the function
518+
// from lowering RTN itself.
519+
//
520+
// For example, in `where for<'a> <T as Trait<'a>>::method(..): Other`,
521+
// the `late_bound_vars` of the where clause predicate (i.e. this HIR ty's
522+
// parent) will include `'a` AND all the early- and late-bound vars of the
523+
// method. But when lowering the RTN type, we just want the list of vars
524+
// we used to resolve the trait ref. We explicitly stored those back onto
525+
// the item segment, since there's no other good place to put them.
511526
let candidate =
512527
ty::Binder::bind_with_vars(trait_ref, tcx.late_bound_vars(item_segment.hir_id));
513528

@@ -539,7 +554,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
539554
}
540555
}
541556

542-
// TODO:
557+
/// Perform type-dependent lookup for a *method* for return type notation.
558+
/// This generally mirrors `<dyn HirTyLowerer>::lower_assoc_path`.
543559
fn resolve_type_relative_return_type_notation(
544560
&self,
545561
qself: &'tcx hir::Ty<'tcx>,
@@ -592,12 +608,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
592608
_ => todo!(),
593609
};
594610

595-
// Don't let `T::method` resolve to some `for<'a> <T as Tr<'a>>::method`.
611+
// Don't let `T::method` resolve to some `for<'a> <T as Tr<'a>>::method`,
612+
// which may happen via a higher-ranked where clause or supertrait.
596613
// This is the same restrictions as associated types; even though we could
597614
// support it, it just makes things a lot more difficult to support in
598-
// `resolve_bound_vars`.
615+
// `resolve_bound_vars`, since we'd need to introduce those as elided
616+
// bound vars on the where clause too.
599617
if bound.has_bound_vars() {
600-
todo!();
618+
return Err(self.tcx().dcx().emit_err(
619+
errors::AssociatedItemTraitUninferredGenericParams {
620+
span,
621+
inferred_sugg: Some(span.with_hi(item_segment.ident.span.lo())),
622+
bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder(),),
623+
mpart_sugg: None,
624+
what: "function",
625+
},
626+
));
601627
}
602628

603629
let trait_def_id = bound.def_id();
@@ -608,7 +634,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
608634
Ok((bound, assoc_ty.def_id))
609635
}
610636

611-
// TODO:
637+
/// Do the common parts of lowering an RTN type. This involves extending the
638+
/// candidate binder to include all of the early- and late-bound vars that are
639+
/// defined on the function itself, and constructing a projection to the RPITIT
640+
/// return type of that function.
612641
fn lower_return_type_notation_ty(
613642
&self,
614643
candidate: ty::PolyTraitRef<'tcx>,

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
20692069
};
20702070
self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr)
20712071
}
2072+
// If we encounter a fully qualified path with RTN generics, then it must have
2073+
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
2074+
// it's certainly in an illegal position.
2075+
hir::TyKind::Path(hir::QPath::Resolved(_, path))
2076+
if path.segments.last().and_then(|segment| segment.args).is_some_and(|args| {
2077+
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
2078+
}) =>
2079+
{
2080+
let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span });
2081+
Ty::new_error(tcx, guar)
2082+
}
20722083
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
20732084
debug!(?maybe_qself, ?path);
20742085
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
@@ -2093,7 +2104,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
20932104
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
20942105
}
20952106
}
2096-
// TODO:
2107+
// If we encounter a type relative path with RTN generics, then it must have
2108+
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
2109+
// it's certainly in an illegal position.
20972110
hir::TyKind::Path(hir::QPath::TypeRelative(_, segment))
20982111
if segment.args.is_some_and(|args| {
20992112
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)

compiler/rustc_resolve/src/late.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,9 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
790790
TyKind::Path(qself, path) => {
791791
self.diag_metadata.current_type_path = Some(ty);
792792

793-
// TODO:
793+
// If we have a path that ends with `(..)`, then it must be
794+
// return type notation. Resolve that path in the *value*
795+
// namespace.
794796
let source = if let Some(seg) = path.segments.last()
795797
&& let Some(args) = &seg.args
796798
&& matches!(**args, GenericArgs::ParenthesizedElided(..))

0 commit comments

Comments
 (0)