1
Fork 0

Rollup merge of #129629 - compiler-errors:rtn-in-path, r=jackh726

Implement Return Type Notation (RTN)'s path form in where clauses

Implement return type notation (RTN) in path position for where clauses. We already had RTN in associated type position ([e.g.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=627a4fb8e2cb334863fbd08ed3722c09)), but per [the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#where-rtn-can-be-used-for-now):

> As a standalone type, RTN can only be used as the Self type of a where-clause [...]

Specifically, in order to enable code like:

```rust
trait Foo {
    fn bar() -> impl Sized;
}

fn is_send(_: impl Send) {}

fn test<T>()
where
    T: Foo,
    T::bar(..): Send,
{
    is_send(T::bar());
}
```

* In the resolver, when we see a `TyKind::Path` whose final segment is `GenericArgs::ParenthesizedElided` (i.e. `(..)`), resolve that path in the *value* namespace, since we're looking for a method.
* When lowering where clauses in HIR lowering, we first try to intercept an RTN self type via `lower_ty_maybe_return_type_notation`. If we find an RTN type, we lower it manually in a way that respects its higher-ranked-ness (see below) and resolves to the corresponding RPITIT. Anywhere else, we'll emit the same "return type notation not allowed in this position yet" error we do when writing RTN in every other position.
* In `resolve_bound_vars`, we add some special treatment for RTN types in where clauses. Specifically, we need to add new lifetime variables to our binders for the early- and late-bound vars we encounter on the method. This implements the higher-ranked desugaring [laid out in the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds).

This PR also adds a bunch of tests, mostly negative ones (testing error messages).

In a follow-up PR, I'm going to mark RTN as no longer incomplete, since this PR basically finishes the impl surface that we should initially stabilize, and the RFC was accepted.

cc [RFC 3654](https://github.com/rust-lang/rfcs/pull/3654) and https://github.com/rust-lang/rust/issues/109417
This commit is contained in:
Michael Goulet 2024-09-21 15:18:56 -04:00 committed by GitHub
commit d72d44d8ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 1493 additions and 229 deletions

View file

@ -20,8 +20,8 @@ use super::errors::{
};
use super::LoweringContext;
use crate::{
fluent_generated as fluent, ImplTraitContext, ImplTraitPosition, ParamMode,
ResolverAstLoweringExt,
fluent_generated as fluent, AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition,
ParamMode, ResolverAstLoweringExt,
};
impl<'a, 'hir> LoweringContext<'a, 'hir> {
@ -201,6 +201,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&sym.qself,
&sym.path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

View file

@ -52,7 +52,7 @@ use rustc_target::spec::abi;
use {rustc_ast as ast, rustc_hir as hir};
use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode};
use crate::{ImplTraitPosition, ResolverAstLoweringExt};
use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt};
pub(crate) struct DelegationResults<'hir> {
pub body_id: hir::BodyId,
@ -340,6 +340,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
&delegation.qself,
&delegation.path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

View file

@ -23,7 +23,7 @@ use super::{
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
};
use crate::errors::YieldInClosure;
use crate::{fluent_generated, FnDeclKind, ImplTraitPosition};
use crate::{fluent_generated, AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition};
impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
@ -281,6 +281,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
@ -328,6 +329,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
&se.qself,
&se.path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
)),
@ -1291,6 +1293,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
@ -1311,6 +1314,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
@ -1336,6 +1340,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
&se.qself,
&se.path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

View file

@ -485,9 +485,23 @@ enum ParamMode {
Optional,
}
#[derive(Copy, Clone, Debug)]
enum AllowReturnTypeNotation {
/// Only in types, since RTN is denied later during HIR lowering.
Yes,
/// All other positions (path expr, method, use tree).
No,
}
enum GenericArgsMode {
/// Allow paren sugar, don't allow RTN.
ParenSugar,
/// Allow RTN, don't allow paren sugar.
ReturnTypeNotation,
// Error if parenthesized generics or RTN are encountered.
Err,
/// Silence errors when lowering generics. Only used with `Res::Err`.
Silence,
}
impl<'a, 'hir> LoweringContext<'a, 'hir> {
@ -1226,7 +1240,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
let id = self.lower_node_id(t.id);
let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx, None);
let qpath = self.lower_qpath(
t.id,
qself,
path,
param_mode,
AllowReturnTypeNotation::Yes,
itctx,
None,
);
self.ty_path(id, t.span, qpath)
}
@ -2203,6 +2225,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&None,
&p.path,
ParamMode::Explicit,
AllowReturnTypeNotation::No,
itctx,
Some(modifiers),
) {
@ -2341,6 +2364,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&None,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
@ -2419,6 +2443,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

View file

@ -11,7 +11,7 @@ use super::errors::{
ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
};
use super::{ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt};
use crate::ImplTraitPosition;
use crate::{AllowReturnTypeNotation, ImplTraitPosition};
impl<'a, 'hir> LoweringContext<'a, 'hir> {
pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> {
@ -38,6 +38,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
@ -55,6 +56,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
@ -66,6 +68,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
qself,
path,
ParamMode::Optional,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

View file

@ -5,6 +5,7 @@ use rustc_hir::def::{DefKind, PartialRes, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
use rustc_middle::span_bug;
use rustc_session::parse::add_feature_diagnostics;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, DesugaringKind, Span, Symbol, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
@ -15,10 +16,9 @@ use super::errors::{
GenericTypeWithParentheses, UseAngleBrackets,
};
use super::{
GenericArgsCtor, GenericArgsMode, ImplTraitContext, LifetimeRes, LoweringContext, ParamMode,
ResolverAstLoweringExt,
AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition,
LifetimeRes, LoweringContext, ParamMode, ResolverAstLoweringExt,
};
use crate::ImplTraitPosition;
impl<'a, 'hir> LoweringContext<'a, 'hir> {
#[instrument(level = "trace", skip(self))]
@ -28,6 +28,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
qself: &Option<ptr::P<QSelf>>,
p: &Path,
param_mode: ParamMode,
allow_return_type_notation: AllowReturnTypeNotation,
itctx: ImplTraitContext,
// modifiers of the impl/bound if this is a trait path
modifiers: Option<ast::TraitBoundModifiers>,
@ -103,8 +104,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
{
GenericArgsMode::ParenSugar
}
Res::Def(DefKind::AssocFn, _) if i + 1 == proj_start => {
match allow_return_type_notation {
AllowReturnTypeNotation::Yes => GenericArgsMode::ReturnTypeNotation,
AllowReturnTypeNotation::No => GenericArgsMode::Err,
}
}
// Avoid duplicated errors.
Res::Err => GenericArgsMode::ParenSugar,
Res::Err => GenericArgsMode::Silence,
// An error
_ => GenericArgsMode::Err,
};
@ -164,11 +171,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// 3. `<<std::vec::Vec<T>>::IntoIter>::Item`
// * final path is `<<<std::vec::Vec<T>>::IntoIter>::Item>::clone`
for (i, segment) in p.segments.iter().enumerate().skip(proj_start) {
// If this is a type-dependent `T::method(..)`.
let generic_args_mode = if i + 1 == p.segments.len()
&& matches!(allow_return_type_notation, AllowReturnTypeNotation::Yes)
{
GenericArgsMode::ReturnTypeNotation
} else {
GenericArgsMode::Err
};
let hir_segment = self.arena.alloc(self.lower_path_segment(
p.span,
segment,
param_mode,
GenericArgsMode::Err,
generic_args_mode,
itctx,
None,
));
@ -238,11 +254,46 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
}
GenericArgs::Parenthesized(data) => match generic_args_mode {
GenericArgsMode::ParenSugar => self.lower_parenthesized_parameter_data(
data,
itctx,
bound_modifier_allowed_features,
),
GenericArgsMode::ReturnTypeNotation => {
let mut err = if !data.inputs.is_empty() {
self.dcx().create_err(BadReturnTypeNotation::Inputs {
span: data.inputs_span,
})
} else if let FnRetTy::Ty(ty) = &data.output {
self.dcx().create_err(BadReturnTypeNotation::Output {
span: data.inputs_span.shrink_to_hi().to(ty.span),
})
} else {
self.dcx().create_err(BadReturnTypeNotation::NeedsDots {
span: data.inputs_span,
})
};
if !self.tcx.features().return_type_notation
&& self.tcx.sess.is_nightly_build()
{
add_feature_diagnostics(
&mut err,
&self.tcx.sess,
sym::return_type_notation,
);
}
err.emit();
(
GenericArgsCtor {
args: Default::default(),
constraints: &[],
parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,
span: path_span,
},
false,
)
}
GenericArgsMode::ParenSugar | GenericArgsMode::Silence => self
.lower_parenthesized_parameter_data(
data,
itctx,
bound_modifier_allowed_features,
),
GenericArgsMode::Err => {
// Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`
let sub = if !data.inputs.is_empty() {
@ -279,7 +330,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
},
GenericArgs::ParenthesizedElided(span) => {
self.dcx().emit_err(BadReturnTypeNotation::Position { span: *span });
match generic_args_mode {
GenericArgsMode::ReturnTypeNotation | GenericArgsMode::Silence => {
// Ok
}
GenericArgsMode::ParenSugar | GenericArgsMode::Err => {
self.dcx().emit_err(BadReturnTypeNotation::Position { span: *span });
}
}
(
GenericArgsCtor {
args: Default::default(),

View file

@ -37,7 +37,7 @@ hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here
hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated type of a trait with uninferred generic parameters
hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated {$what} of a trait with uninferred generic parameters
.suggestion = use a fully qualified path with inferred lifetimes
hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes
@ -48,6 +48,8 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh
hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found}
hir_analysis_bad_return_type_notation_position = return type notation not allowed in this position yet
hir_analysis_cannot_capture_late_bound_const =
cannot capture late-bound const parameter in {$what}
.label = parameter defined here

View file

@ -460,7 +460,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
[] => (generics.span, format!("<{lt_name}>")),
[bound, ..] => (bound.span.shrink_to_lo(), format!("{lt_name}, ")),
};
mpart_sugg = Some(errors::AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion {
mpart_sugg = Some(errors::AssociatedItemTraitUninferredGenericParamsMultipartSuggestion {
fspan: lt_sp,
first: sugg,
sspan: span.with_hi(item_segment.ident.span.lo()),
@ -502,11 +502,12 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
}
Ty::new_error(
self.tcx(),
self.tcx().dcx().emit_err(errors::AssociatedTypeTraitUninferredGenericParams {
self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
span,
inferred_sugg,
bound,
mpart_sugg,
what: "type",
}),
)
}

View file

@ -240,7 +240,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
for predicate in hir_generics.predicates {
match predicate {
hir::WherePredicate::BoundPredicate(bound_pred) => {
let ty = icx.lower_ty(bound_pred.bounded_ty);
let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty);
let bound_vars = tcx.late_bound_vars(bound_pred.hir_id);
// Keep the type around in a dummy predicate, in case of no bounds.
// That way, `where Ty:` is not a complete noop (see #53696) and `Ty`
@ -770,6 +771,10 @@ impl<'tcx> ItemCtxt<'tcx> {
continue;
};
// Subtle: If we're collecting `SelfAndAssociatedTypeBounds`, then we
// want to only consider predicates with `Self: ...`, but we don't want
// `OnlySelfBounds(true)` since we want to collect the nested associated
// type bound as well.
let (only_self_bounds, assoc_name) = match filter {
PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {
(OnlySelfBounds(false), None)
@ -780,14 +785,10 @@ impl<'tcx> ItemCtxt<'tcx> {
}
};
// Subtle: If we're collecting `SelfAndAssociatedTypeBounds`, then we
// want to only consider predicates with `Self: ...`, but we don't want
// `OnlySelfBounds(true)` since we want to collect the nested associated
// type bound as well.
let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) {
ty
} else if matches!(filter, PredicateFilter::All) {
self.lower_ty(predicate.bounded_ty)
self.lowerer().lower_ty_maybe_return_type_notation(predicate.bounded_ty)
} else {
continue;
};

View file

@ -889,7 +889,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
(pair, r)
})
.unzip();
self.record_late_bound_vars(hir_id, binders);
// If this is an RTN type in the self type, then append those to the binder.
self.try_append_return_type_notation_params(hir_id, bounded_ty);
// Even if there are no lifetimes defined here, we still wrap it in a binder
// scope. If there happens to be a nested poly trait ref (an error), that
// will be `Concatenating` anyways, so we don't have to worry about the depth
@ -1635,9 +1640,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
// }
// ```
// and a bound that looks like:
// `for<'a> T::Trait<'a, x(): for<'b> Other<'b>>`
// `for<'a> T::Trait<'a, x(..): for<'b> Other<'b>>`
// this is going to expand to something like:
// `for<'a> for<'r, T> <T as Trait<'a>>::x::<'r, T>::{opaque#0}: for<'b> Other<'b>`.
// `for<'a> for<'r> <T as Trait<'a>>::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
@ -1839,6 +1844,178 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id);
assert_eq!(old_value, Some(bad_def));
}
// When we have a return type notation type in a where clause, like
// `where <T as Trait>::method(..): Send`, we need to introduce new bound
// vars to the existing where clause's binder, to represent the lifetimes
// elided by the return-type-notation syntax.
//
// For example, given
// ```
// trait Foo {
// async fn x<'r>();
// }
// ```
// and a bound that looks like:
// `for<'a, 'b> <T as Trait<'a>>::x(): Other<'b>`
// this is going to expand to something like:
// `for<'a, 'b, 'r> <T as Trait<'a>>::x::<'r, T>::{opaque#0}: Other<'b>`.
//
// We handle this similarly for associated-type-bound style return-type-notation
// in `visit_segment_args`.
fn try_append_return_type_notation_params(
&mut self,
hir_id: HirId,
hir_ty: &'tcx hir::Ty<'tcx>,
) {
let hir::TyKind::Path(qpath) = hir_ty.kind else {
// We only care about path types here. All other self types
// (including nesting the RTN type in another type) don't do
// anything.
return;
};
let (mut bound_vars, item_def_id, item_segment) = match qpath {
// If we have a fully qualified method, then we don't need to do any special lookup.
hir::QPath::Resolved(_, path)
if let [.., item_segment] = &path.segments[..]
&& item_segment.args.is_some_and(|args| {
matches!(
args.parenthesized,
hir::GenericArgsParentheses::ReturnTypeNotation
)
}) =>
{
match path.res {
Res::Err => return,
Res::Def(DefKind::AssocFn, item_def_id) => (vec![], item_def_id, item_segment),
_ => bug!("only expected method resolution for fully qualified RTN"),
}
}
// If we have a type-dependent path, then we do need to do some lookup.
hir::QPath::TypeRelative(qself, item_segment)
if item_segment.args.is_some_and(|args| {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
// First, ignore a qself that isn't a type or `Self` param. Those are the
// only ones that support `T::Assoc` anyways in HIR lowering.
let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind else {
return;
};
match path.res {
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => {
// Get the generics of this type's hir owner. This is *different*
// from the generics of the parameter's definition, since we want
// to be able to resolve an RTN path on a nested body (e.g. method
// inside an impl) using the where clauses on the method.
// FIXME(return_type_notation): Think of some better way of doing this.
let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics()
else {
return;
};
// Look for the first bound that contains an associated type that
// matches the segment that we're looking for. We ignore any subsequent
// bounds since we'll be emitting a hard error in HIR lowering, so this
// is purely speculative.
let one_bound = generics.predicates.iter().find_map(|predicate| {
let hir::WherePredicate::BoundPredicate(predicate) = predicate else {
return None;
};
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
predicate.bounded_ty.kind
else {
return None;
};
if bounded_path.res != path.res {
return None;
}
predicate.bounds.iter().find_map(|bound| {
let hir::GenericBound::Trait(trait_, _) = bound else {
return None;
};
BoundVarContext::supertrait_hrtb_vars(
self.tcx,
trait_.trait_ref.trait_def_id()?,
item_segment.ident,
ty::AssocKind::Fn,
)
})
});
let Some((bound_vars, assoc_item)) = one_bound else {
return;
};
(bound_vars, assoc_item.def_id, item_segment)
}
// If we have a self type alias (in an impl), try to resolve an
// associated item from one of the supertraits of the impl's trait.
Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. } => {
let hir::ItemKind::Impl(hir::Impl { of_trait: Some(trait_ref), .. }) = self
.tcx
.hir_node_by_def_id(impl_def_id.expect_local())
.expect_item()
.kind
else {
return;
};
let Some(trait_def_id) = trait_ref.trait_def_id() else {
return;
};
let Some((bound_vars, assoc_item)) = BoundVarContext::supertrait_hrtb_vars(
self.tcx,
trait_def_id,
item_segment.ident,
ty::AssocKind::Fn,
) else {
return;
};
(bound_vars, assoc_item.def_id, item_segment)
}
_ => return,
}
}
_ => return,
};
// Append the early-bound vars on the function, and then the late-bound ones.
// We actually turn type parameters into higher-ranked types here, but we
// deny them later in HIR lowering.
bound_vars.extend(self.tcx.generics_of(item_def_id).own_params.iter().map(|param| {
match param.kind {
ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
ty::BoundRegionKind::BrNamed(param.def_id, param.name),
),
ty::GenericParamDefKind::Type { .. } => {
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id, param.name))
}
ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
}
}));
bound_vars.extend(self.tcx.fn_sig(item_def_id).instantiate_identity().bound_vars());
// SUBTLE: Stash the old bound vars onto the *item segment* before appending
// the new bound vars. We do this because we need to know how many bound vars
// are present on the binder explicitly (i.e. not return-type-notation vars)
// to do bound var shifting correctly in HIR lowering.
//
// 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.
//
// See where these vars are used in `HirTyLowerer::lower_ty_maybe_return_type_notation`.
// And this is exercised in:
// `tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs`.
let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id).unwrap();
let existing_bound_vars_saved = existing_bound_vars.clone();
existing_bound_vars.extend(bound_vars);
self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved);
}
}
/// Detects late-bound lifetimes and inserts them into

View file

@ -780,14 +780,15 @@ pub(crate) struct PlaceholderNotAllowedItemSignatures {
#[derive(Diagnostic)]
#[diag(hir_analysis_associated_type_trait_uninferred_generic_params, code = E0212)]
pub(crate) struct AssociatedTypeTraitUninferredGenericParams {
pub(crate) struct AssociatedItemTraitUninferredGenericParams {
#[primary_span]
pub span: Span,
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "{bound}")]
pub inferred_sugg: Option<Span>,
pub bound: String,
#[subdiagnostic]
pub mpart_sugg: Option<AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion>,
pub mpart_sugg: Option<AssociatedItemTraitUninferredGenericParamsMultipartSuggestion>,
pub what: &'static str,
}
#[derive(Subdiagnostic)]
@ -795,7 +796,7 @@ pub(crate) struct AssociatedTypeTraitUninferredGenericParams {
hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion,
applicability = "maybe-incorrect"
)]
pub(crate) struct AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion {
pub(crate) struct AssociatedItemTraitUninferredGenericParamsMultipartSuggestion {
#[suggestion_part(code = "{first}")]
pub fspan: Span,
pub first: String,
@ -1693,3 +1694,10 @@ pub(crate) struct CmseCallGeneric {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_bad_return_type_notation_position)]
pub(crate) struct BadReturnTypeNotation {
#[primary_span]
pub span: Span,
}

View file

@ -6,6 +6,7 @@ use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::HirId;
use rustc_middle::bug;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
use rustc_span::symbol::Ident;
@ -15,6 +16,7 @@ use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
use smallvec::SmallVec;
use tracing::{debug, instrument};
use super::errors::GenericsArgsErrExtend;
use crate::bounds::Bounds;
use crate::errors;
use crate::hir_ty_lowering::{
@ -332,74 +334,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.or_insert(constraint.span);
let projection_term = if let ty::AssocKind::Fn = assoc_kind {
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, assoc_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::BrNamed(param.def_id, param.name),
},
)
.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(assoc_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.into()
} else {
return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
span: constraint.span,
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
fn_span: tcx.hir().span_if_local(assoc_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);
let instantiation_output = ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args);
let bound_vars = tcx.late_bound_vars(constraint.hir_id);
ty::Binder::bind_with_vars(instantiation_output, bound_vars)
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
@ -525,6 +464,269 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
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 {
return self.error_missing_qpath_self_ty(
trait_def_id,
hir_ty.span,
item_segment,
);
};
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,
ty::BoundConstness::NotConst,
);
// 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(qself, item_segment)
if item_segment.args.is_some_and(|args| {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
match self
.resolve_type_relative_return_type_notation(
qself,
item_segment,
hir_ty.hir_id,
hir_ty.span,
)
.and_then(|(candidate, item_def_id)| {
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),
}
}
_ => self.lower_ty(hir_ty),
}
}
/// Perform type-dependent lookup for a *method* for return type notation.
/// This generally mirrors `<dyn HirTyLowerer>::lower_assoc_path`.
fn resolve_type_relative_return_type_notation(
&self,
qself: &'tcx hir::Ty<'tcx>,
item_segment: &'tcx hir::PathSegment<'tcx>,
qpath_hir_id: HirId,
span: Span,
) -> Result<(ty::PolyTraitRef<'tcx>, DefId), ErrorGuaranteed> {
let tcx = self.tcx();
let qself_ty = self.lower_ty(qself);
let assoc_ident = item_segment.ident;
let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
};
let bound = match (qself_ty.kind(), qself_res) {
(_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => {
// `Self` in an impl of a trait -- we have a concrete self type and a
// trait reference.
let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
// A cycle error occurred, most likely.
self.dcx().span_bug(span, "expected cycle error");
};
self.probe_single_bound_for_assoc_item(
|| {
traits::supertraits(
tcx,
ty::Binder::dummy(trait_ref.instantiate_identity()),
)
},
AssocItemQSelf::SelfTyAlias,
ty::AssocKind::Fn,
assoc_ident,
span,
None,
)?
}
(
&ty::Param(_),
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
) => self.probe_single_ty_param_bound_for_assoc_item(
param_did.expect_local(),
qself.span,
ty::AssocKind::Fn,
assoc_ident,
span,
)?,
_ => {
if let Err(reported) = qself_ty.error_reported() {
return Err(reported);
} else {
// FIXME(return_type_notation): Provide some structured suggestion here.
let err = struct_span_code_err!(
self.dcx(),
span,
E0223,
"ambiguous associated function"
);
return Err(err.emit());
}
}
};
// 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 Err(self.tcx().dcx().emit_err(
errors::AssociatedItemTraitUninferredGenericParams {
span,
inferred_sugg: Some(span.with_hi(item_segment.ident.span.lo())),
bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder(),),
mpart_sugg: None,
what: "function",
},
));
}
let trait_def_id = bound.def_id();
let assoc_ty = self
.probe_assoc_item(assoc_ident, ty::AssocKind::Fn, qpath_hir_id, span, trait_def_id)
.expect("failed to find associated type");
Ok((bound, assoc_ty.def_id))
}
/// 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::BrNamed(param.def_id, param.name),
},
)
.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.

View file

@ -53,7 +53,7 @@ use rustc_trait_selection::traits::{self, ObligationCtxt};
use tracing::{debug, debug_span, instrument};
use crate::bounds::Bounds;
use crate::errors::{AmbiguousLifetimeBound, WildPatTy};
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, WildPatTy};
use crate::hir_ty_lowering::errors::{prohibit_assoc_item_constraint, GenericsArgsErrExtend};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
@ -813,17 +813,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
/// Search for a trait bound on a type parameter whose trait defines the associated type given by `assoc_name`.
/// Search for a trait bound on a type parameter whose trait defines the associated item
/// given by `assoc_name` and `kind`.
///
/// This fails if there is no such bound in the list of candidates or if there are multiple
/// candidates in which case it reports ambiguity.
///
/// `ty_param_def_id` is the `LocalDefId` of the type parameter.
#[instrument(level = "debug", skip_all, ret)]
fn probe_single_ty_param_bound_for_assoc_ty(
fn probe_single_ty_param_bound_for_assoc_item(
&self,
ty_param_def_id: LocalDefId,
ty_param_span: Span,
kind: ty::AssocKind,
assoc_name: Ident,
span: Span,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
@ -841,7 +843,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
},
AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span),
ty::AssocKind::Type,
kind,
assoc_name,
span,
None,
@ -1081,9 +1083,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
(
&ty::Param(_),
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
) => self.probe_single_ty_param_bound_for_assoc_ty(
) => self.probe_single_ty_param_bound_for_assoc_item(
param_did.expect_local(),
qself.span,
ty::AssocKind::Type,
assoc_ident,
span,
)?,
@ -1545,48 +1548,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
debug!(?trait_def_id);
let Some(self_ty) = opt_self_ty else {
let path_str = tcx.def_path_str(trait_def_id);
let def_id = self.item_def_id();
debug!(item_def_id = ?def_id);
// FIXME: document why/how this is different from `tcx.local_parent(def_id)`
let parent_def_id =
tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id();
debug!(?parent_def_id);
// If the trait in segment is the same as the trait defining the item,
// use the `<Self as ..>` syntax in the error.
let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id;
let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id;
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
vec!["Self".to_string()]
} else {
// Find all the types that have an `impl` for the trait.
tcx.all_impls(trait_def_id)
.filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id))
.filter(|header| {
// Consider only accessible traits
tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
&& header.polarity != ty::ImplPolarity::Negative
})
.map(|header| header.trait_ref.instantiate_identity().self_ty())
// We don't care about blanket impls.
.filter(|self_ty| !self_ty.has_non_region_param())
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
.collect()
};
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
// references the trait. Relevant for the first case in
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
let reported = self.report_ambiguous_assoc_ty(
span,
&type_names,
&[path_str],
item_segment.ident.name,
);
return Ty::new_error(tcx, reported);
return self.error_missing_qpath_self_ty(trait_def_id, span, item_segment);
};
debug!(?self_ty);
@ -1600,6 +1562,53 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ty::new_projection_from_args(tcx, item_def_id, item_args)
}
fn error_missing_qpath_self_ty(
&self,
trait_def_id: DefId,
span: Span,
item_segment: &hir::PathSegment<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx();
let path_str = tcx.def_path_str(trait_def_id);
let def_id = self.item_def_id();
debug!(item_def_id = ?def_id);
// FIXME: document why/how this is different from `tcx.local_parent(def_id)`
let parent_def_id =
tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id();
debug!(?parent_def_id);
// If the trait in segment is the same as the trait defining the item,
// use the `<Self as ..>` syntax in the error.
let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id;
let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id;
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
vec!["Self".to_string()]
} else {
// Find all the types that have an `impl` for the trait.
tcx.all_impls(trait_def_id)
.filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id))
.filter(|header| {
// Consider only accessible traits
tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
&& header.polarity != ty::ImplPolarity::Negative
})
.map(|header| header.trait_ref.instantiate_identity().self_ty())
// We don't care about blanket impls.
.filter(|self_ty| !self_ty.has_non_region_param())
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
.collect()
};
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
// references the trait. Relevant for the first case in
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
let reported =
self.report_ambiguous_assoc_ty(span, &type_names, &[path_str], item_segment.ident.name);
Ty::new_error(tcx, reported)
}
pub fn prohibit_generic_args<'a>(
&self,
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
@ -1930,7 +1939,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.tcx()
.dcx()
.span_delayed_bug(path.span, "path with `Res::Err` but no error emitted");
Ty::new_error(self.tcx(), e)
Ty::new_error(tcx, e)
}
Res::Def(..) => {
assert_eq!(
@ -2061,6 +2070,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
};
self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr)
}
// If we encounter a fully qualified path with RTN generics, then it must have
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
// it's certainly in an illegal position.
hir::TyKind::Path(hir::QPath::Resolved(_, path))
if path.segments.last().and_then(|segment| segment.args).is_some_and(|args| {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span });
Ty::new_error(tcx, guar)
}
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
@ -2085,6 +2105,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
}
}
// If we encounter a type relative path with RTN generics, then it must have
// *not* gone through `lower_ty_maybe_return_type_notation`, and therefore
// it's certainly in an illegal position.
hir::TyKind::Path(hir::QPath::TypeRelative(_, segment))
if segment.args.is_some_and(|args| {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span });
Ty::new_error(tcx, guar)
}
hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
debug!(?qself, ?segment);
let ty = self.lower_ty(qself);

View file

@ -404,6 +404,8 @@ pub(crate) enum PathSource<'a> {
Delegation,
/// An arg in a `use<'a, N>` precise-capturing bound.
PreciseCapturingArg(Namespace),
// Paths that end with `(..)`, for return type notation.
ReturnTypeNotation,
}
impl<'a> PathSource<'a> {
@ -413,7 +415,8 @@ impl<'a> PathSource<'a> {
PathSource::Expr(..)
| PathSource::Pat
| PathSource::TupleStruct(..)
| PathSource::Delegation => ValueNS,
| PathSource::Delegation
| PathSource::ReturnTypeNotation => ValueNS,
PathSource::TraitItem(ns) => ns,
PathSource::PreciseCapturingArg(ns) => ns,
}
@ -425,7 +428,8 @@ impl<'a> PathSource<'a> {
| PathSource::Expr(..)
| PathSource::Pat
| PathSource::Struct
| PathSource::TupleStruct(..) => true,
| PathSource::TupleStruct(..)
| PathSource::ReturnTypeNotation => true,
PathSource::Trait(_)
| PathSource::TraitItem(..)
| PathSource::Delegation
@ -471,7 +475,7 @@ impl<'a> PathSource<'a> {
},
_ => "value",
},
PathSource::Delegation => "function",
PathSource::ReturnTypeNotation | PathSource::Delegation => "function",
PathSource::PreciseCapturingArg(..) => "type or const parameter",
}
}
@ -540,6 +544,10 @@ impl<'a> PathSource<'a> {
Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true,
_ => false,
},
PathSource::ReturnTypeNotation => match res {
Res::Def(DefKind::AssocFn, _) => true,
_ => false,
},
PathSource::Delegation => matches!(res, Res::Def(DefKind::Fn | DefKind::AssocFn, _)),
PathSource::PreciseCapturingArg(ValueNS) => {
matches!(res, Res::Def(DefKind::ConstParam, _))
@ -565,8 +573,8 @@ impl<'a> PathSource<'a> {
(PathSource::Expr(..), false) | (PathSource::Delegation, false) => E0425,
(PathSource::Pat | PathSource::TupleStruct(..), true) => E0532,
(PathSource::Pat | PathSource::TupleStruct(..), false) => E0531,
(PathSource::TraitItem(..), true) => E0575,
(PathSource::TraitItem(..), false) => E0576,
(PathSource::TraitItem(..), true) | (PathSource::ReturnTypeNotation, true) => E0575,
(PathSource::TraitItem(..), false) | (PathSource::ReturnTypeNotation, false) => E0576,
(PathSource::PreciseCapturingArg(..), true) => E0799,
(PathSource::PreciseCapturingArg(..), false) => E0800,
}
@ -781,7 +789,20 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
}
TyKind::Path(qself, path) => {
self.diag_metadata.current_type_path = Some(ty);
self.smart_resolve_path(ty.id, qself, path, PathSource::Type);
// If we have a path that ends with `(..)`, then it must be
// return type notation. Resolve that path in the *value*
// namespace.
let source = if let Some(seg) = path.segments.last()
&& let Some(args) = &seg.args
&& matches!(**args, GenericArgs::ParenthesizedElided(..))
{
PathSource::ReturnTypeNotation
} else {
PathSource::Type
};
self.smart_resolve_path(ty.id, qself, path, source);
// Check whether we should interpret this as a bare trait object.
if qself.is_none()
@ -1920,7 +1941,8 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
PathSource::Trait(..)
| PathSource::TraitItem(..)
| PathSource::Type
| PathSource::PreciseCapturingArg(..) => false,
| PathSource::PreciseCapturingArg(..)
| PathSource::ReturnTypeNotation => false,
PathSource::Expr(..)
| PathSource::Pat
| PathSource::Struct

View file

@ -16,4 +16,22 @@ fn bar<T: Trait<method() -> (): Send>>() {}
fn baz<T: Trait<method(): Send>>() {}
//~^ ERROR return type notation arguments must be elided with `..`
fn foo_path<T: Trait>() where T::method(i32): Send {}
//~^ ERROR argument types not allowed with return type notation
fn bar_path<T: Trait>() where T::method() -> (): Send {}
//~^ ERROR return type not allowed with return type notation
fn baz_path<T: Trait>() where T::method(): Send {}
//~^ ERROR return type notation arguments must be elided with `..`
fn foo_qualified<T: Trait>() where <T as Trait>::method(i32): Send {}
//~^ ERROR expected associated type
fn bar_qualified<T: Trait>() where <T as Trait>::method() -> (): Send {}
//~^ ERROR expected associated type
fn baz_qualified<T: Trait>() where <T as Trait>::method(): Send {}
//~^ ERROR expected associated type
fn main() {}

View file

@ -1,3 +1,21 @@
error[E0575]: expected associated type, found associated function `Trait::method`
--> $DIR/bad-inputs-and-output.rs:28:36
|
LL | fn foo_qualified<T: Trait>() where <T as Trait>::method(i32): Send {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type
error[E0575]: expected associated type, found associated function `Trait::method`
--> $DIR/bad-inputs-and-output.rs:31:36
|
LL | fn bar_qualified<T: Trait>() where <T as Trait>::method() -> (): Send {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a associated type
error[E0575]: expected associated type, found associated function `Trait::method`
--> $DIR/bad-inputs-and-output.rs:34:36
|
LL | fn baz_qualified<T: Trait>() where <T as Trait>::method(): Send {}
| ^^^^^^^^^^^^^^^^^^^^^^ not a associated type
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/bad-inputs-and-output.rs:3:12
|
@ -25,5 +43,24 @@ error: return type notation arguments must be elided with `..`
LL | fn baz<T: Trait<method(): Send>>() {}
| ^^ help: add `..`: `(..)`
error: aborting due to 3 previous errors; 1 warning emitted
error: argument types not allowed with return type notation
--> $DIR/bad-inputs-and-output.rs:19:40
|
LL | fn foo_path<T: Trait>() where T::method(i32): Send {}
| ^^^^^ help: remove the input types: `()`
error: return type not allowed with return type notation
--> $DIR/bad-inputs-and-output.rs:22:42
|
LL | fn bar_path<T: Trait>() where T::method() -> (): Send {}
| ^^^^^^ help: remove the return type
error: return type notation arguments must be elided with `..`
--> $DIR/bad-inputs-and-output.rs:25:40
|
LL | fn baz_path<T: Trait>() where T::method(): Send {}
| ^^ help: add `..`: `(..)`
error: aborting due to 9 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0575`.

View file

@ -10,17 +10,12 @@ trait Tr {
fn foo<T: Tr>()
where
T::method(..): Send,
//~^ ERROR return type notation not allowed in this position yet
//~| ERROR expected type, found function
<T as Tr>::method(..): Send,
//~^ ERROR return type notation not allowed in this position yet
//~| ERROR expected associated type, found associated function `Tr::method`
{
let _ = T::CONST::(..);
//~^ ERROR return type notation not allowed in this position yet
let _: T::method(..);
//~^ ERROR return type notation not allowed in this position yet
//~| ERROR expected type, found function
}
fn main() {}

View file

@ -1,9 +1,3 @@
error[E0575]: expected associated type, found associated function `Tr::method`
--> $DIR/bare-path.rs:15:5
|
LL | <T as Tr>::method(..): Send,
| ^^^^^^^^^^^^^^^^^^^^^ not a associated type
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/bare-path.rs:1:12
|
@ -14,53 +8,16 @@ LL | #![feature(return_type_notation)]
= note: `#[warn(incomplete_features)]` on by default
error: return type notation not allowed in this position yet
--> $DIR/bare-path.rs:19:23
--> $DIR/bare-path.rs:15:23
|
LL | let _ = T::CONST::(..);
| ^^^^
error: return type notation not allowed in this position yet
--> $DIR/bare-path.rs:21:21
--> $DIR/bare-path.rs:17:12
|
LL | let _: T::method(..);
| ^^^^
| ^^^^^^^^^^^^^
error: return type notation not allowed in this position yet
--> $DIR/bare-path.rs:12:14
|
LL | T::method(..): Send,
| ^^^^
error: aborting due to 2 previous errors; 1 warning emitted
error: return type notation not allowed in this position yet
--> $DIR/bare-path.rs:15:22
|
LL | <T as Tr>::method(..): Send,
| ^^^^
error: expected type, found function
--> $DIR/bare-path.rs:12:8
|
LL | T::method(..): Send,
| ^^^^^^ unexpected function
|
note: the associated function is defined here
--> $DIR/bare-path.rs:7:5
|
LL | fn method() -> impl Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected type, found function
--> $DIR/bare-path.rs:21:15
|
LL | let _: T::method(..);
| ^^^^^^ unexpected function
|
note: the associated function is defined here
--> $DIR/bare-path.rs:7:5
|
LL | fn method() -> impl Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 7 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0575`.

View file

@ -0,0 +1,52 @@
//@ check-pass
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Trait<'a> {
fn late<'b>(&'b self, _: &'a ()) -> impl Sized;
fn early<'b: 'b>(&'b self, _: &'a ()) -> impl Sized;
}
#[allow(refining_impl_trait_internal)]
impl<'a> Trait<'a> for () {
fn late<'b>(&'b self, _: &'a ()) -> i32 { 1 }
fn early<'b: 'b>(&'b self, _: &'a ()) -> i32 { 1 }
}
trait Other<'c> {}
impl Other<'_> for i32 {}
fn test<T>(t: &T)
where
T: for<'a, 'c> Trait<'a, late(..): Other<'c>>,
// which is basically:
// for<'a, 'c> Trait<'a, for<'b> method<'b>: Other<'c>>,
T: for<'a, 'c> Trait<'a, early(..): Other<'c>>,
// which is basically:
// for<'a, 'c> Trait<'a, for<'b> method<'b>: Other<'c>>,
{
is_other_impl(t.late(&()));
is_other_impl(t.early(&()));
}
fn test_path<T>(t: &T)
where
T: for<'a> Trait<'a>,
for<'a, 'c> <T as Trait<'a>>::late(..): Other<'c>,
// which is basically:
// for<'a, 'b, 'c> <T as Trait<'a>>::method::<'b>: Other<'c>
for<'a, 'c> <T as Trait<'a>>::early(..): Other<'c>,
// which is basically:
// for<'a, 'b, 'c> <T as Trait<'a>>::method::<'b>: Other<'c>
{
is_other_impl(t.late(&()));
is_other_impl(t.early(&()));
}
fn is_other_impl(_: impl for<'c> Other<'c>) {}
fn main() {
test(&());
test(&());
}

View file

@ -0,0 +1,11 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/higher-ranked-bound-works.rs:3:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: 1 warning emitted

View file

@ -0,0 +1,42 @@
//@ check-pass
#![allow(non_camel_case_types)]
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Foo {
type test;
fn test() -> impl Bar;
}
fn call_path<T: Foo>()
where
T::test(..): Bar,
{
}
fn call_bound<T: Foo<test(..): Bar>>() {}
trait Bar {}
struct NotBar;
struct YesBar;
impl Bar for YesBar {}
impl Foo for () {
type test = NotBar;
// Use refinement here so we can observe `YesBar: Bar`.
#[allow(refining_impl_trait_internal)]
fn test() -> YesBar {
YesBar
}
}
fn main() {
// If `T::test(..)` resolved to the GAT (erroneously), then this would be
// an error since `<() as Foo>::bar` -- the associated type -- does not
// implement `Bar`, but the return type of the method does.
call_path::<()>();
call_bound::<()>();
}

View file

@ -0,0 +1,11 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/namespace-conflict.rs:4:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: 1 warning emitted

View file

@ -5,7 +5,10 @@ trait Trait {
fn method() {}
}
fn test<T: Trait<method(..): Send>>() {}
//~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait`
fn bound<T: Trait<method(..): Send>>() {}
//~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait`
fn path<T>() where T: Trait, T::method(..): Send {}
//~^ ERROR return type notation used on function that is not `async` and does not return `impl Trait`
fn main() {}

View file

@ -8,15 +8,26 @@ LL | #![feature(return_type_notation)]
= note: `#[warn(incomplete_features)]` on by default
error: return type notation used on function that is not `async` and does not return `impl Trait`
--> $DIR/non-rpitit.rs:8:18
--> $DIR/non-rpitit.rs:8:19
|
LL | fn method() {}
| ----------- this function must be `async` or return `impl Trait`
...
LL | fn test<T: Trait<method(..): Send>>() {}
| ^^^^^^^^^^^^^^^^
LL | fn bound<T: Trait<method(..): Send>>() {}
| ^^^^^^^^^^^^^^^^
|
= note: function returns `()`, which is not compatible with associated type return bounds
error: aborting due to 1 previous error; 1 warning emitted
error: return type notation used on function that is not `async` and does not return `impl Trait`
--> $DIR/non-rpitit.rs:11:30
|
LL | fn method() {}
| ----------- this function must be `async` or return `impl Trait`
...
LL | fn path<T>() where T: Trait, T::method(..): Send {}
| ^^^^^^^^^^^^^
|
= note: function returns `()`, which is not compatible with associated type return bounds
error: aborting due to 2 previous errors; 1 warning emitted

View file

@ -0,0 +1,42 @@
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
fn function() {}
fn not_a_method()
where
function(..): Send,
//~^ ERROR expected function, found function `function`
//~| ERROR return type notation not allowed in this position yet
{
}
fn not_a_method_and_typoed()
where
function(): Send,
//~^ ERROR expected type, found function `function`
{
}
trait Tr {
fn method();
}
// Forgot the `T::`
fn maybe_method_overlaps<T: Tr>()
where
method(..): Send,
//~^ ERROR cannot find function `method` in this scope
//~| ERROR return type notation not allowed in this position yet
{
}
// Forgot the `T::`, AND typoed `(..)` to `()`
fn maybe_method_overlaps_and_typoed<T: Tr>()
where
method(): Send,
//~^ ERROR cannot find type `method` in this scope
{
}
fn main() {}

View file

@ -0,0 +1,49 @@
error[E0575]: expected function, found function `function`
--> $DIR/not-a-method.rs:8:5
|
LL | function(..): Send,
| ^^^^^^^^^^^^ not a function
error[E0573]: expected type, found function `function`
--> $DIR/not-a-method.rs:16:5
|
LL | function(): Send,
| ^^^^^^^^^^ not a type
error[E0576]: cannot find function `method` in this scope
--> $DIR/not-a-method.rs:28:5
|
LL | method(..): Send,
| ^^^^^^ not found in this scope
error[E0412]: cannot find type `method` in this scope
--> $DIR/not-a-method.rs:37:5
|
LL | method(): Send,
| ^^^^^^ not found in this scope
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/not-a-method.rs:1:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
error: return type notation not allowed in this position yet
--> $DIR/not-a-method.rs:8:5
|
LL | function(..): Send,
| ^^^^^^^^^^^^
error: return type notation not allowed in this position yet
--> $DIR/not-a-method.rs:28:5
|
LL | method(..): Send,
| ^^^^^^^^^^
error: aborting due to 6 previous errors; 1 warning emitted
Some errors have detailed explanations: E0412, E0573, E0575, E0576.
For more information about an error, try `rustc --explain E0412`.

View file

@ -0,0 +1,27 @@
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait A {
fn method() -> impl Sized;
}
trait B {
fn method() -> impl Sized;
}
fn ambiguous<T: A + B>()
where
T::method(..): Send,
//~^ ERROR ambiguous associated function `method` in bounds of `T`
{
}
trait Sub: A + B {}
fn ambiguous_via_supertrait<T: Sub>()
where
T::method(..): Send,
//~^ ERROR ambiguous associated function `method` in bounds of `T`
{
}
fn main() {}

View file

@ -0,0 +1,54 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-ambiguous.rs:1:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0221]: ambiguous associated function `method` in bounds of `T`
--> $DIR/path-ambiguous.rs:13:5
|
LL | fn method() -> impl Sized;
| -------------------------- ambiguous `method` from `A`
...
LL | fn method() -> impl Sized;
| -------------------------- ambiguous `method` from `B`
...
LL | T::method(..): Send,
| ^^^^^^^^^^^^^ ambiguous associated function `method`
|
help: use fully-qualified syntax to disambiguate
|
LL | <T as B>::method(..): Send,
| ~~~~~~~~~~
help: use fully-qualified syntax to disambiguate
|
LL | <T as A>::method(..): Send,
| ~~~~~~~~~~
error[E0221]: ambiguous associated function `method` in bounds of `T`
--> $DIR/path-ambiguous.rs:22:5
|
LL | fn method() -> impl Sized;
| -------------------------- ambiguous `method` from `A`
...
LL | fn method() -> impl Sized;
| -------------------------- ambiguous `method` from `B`
...
LL | T::method(..): Send,
| ^^^^^^^^^^^^^ ambiguous associated function `method`
|
help: use fully-qualified syntax to disambiguate
|
LL | <T as B>::method(..): Send,
| ~~~~~~~~~~
help: use fully-qualified syntax to disambiguate
|
LL | <T as A>::method(..): Send,
| ~~~~~~~~~~
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0221`.

View file

@ -0,0 +1,24 @@
//@ check-pass
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Trait {
fn method() -> impl Sized;
}
fn is_send(_: impl Send) {}
struct W<T>(T);
impl<T> W<T> {
fn test()
where
T: Trait,
T::method(..): Send,
{
is_send(T::method());
}
}
fn main() {}

View file

@ -0,0 +1,11 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-constrained-in-method.rs:3:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: 1 warning emitted

View file

@ -0,0 +1,25 @@
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait A<'a> {
fn method() -> impl Sized;
}
trait B: for<'a> A<'a> {}
fn higher_ranked<T>()
where
T: for<'a> A<'a>,
T::method(..): Send,
//~^ ERROR cannot use the associated function of a trait with uninferred generic parameters
{
}
fn higher_ranked_via_supertrait<T>()
where
T: B,
T::method(..): Send,
//~^ ERROR cannot use the associated function of a trait with uninferred generic parameters
{
}
fn main() {}

View file

@ -0,0 +1,34 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-higher-ranked.rs:1:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0212]: cannot use the associated function of a trait with uninferred generic parameters
--> $DIR/path-higher-ranked.rs:12:5
|
LL | T::method(..): Send,
| ^^^^^^^^^^^^^
|
help: use a fully qualified path with inferred lifetimes
|
LL | <T as A<'_>>::method(..): Send,
| ~~~~~~~~~~~~~~
error[E0212]: cannot use the associated function of a trait with uninferred generic parameters
--> $DIR/path-higher-ranked.rs:20:5
|
LL | T::method(..): Send,
| ^^^^^^^^^^^^^
|
help: use a fully qualified path with inferred lifetimes
|
LL | <T as A<'_>>::method(..): Send,
| ~~~~~~~~~~~~~~
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0212`.

View file

@ -0,0 +1,25 @@
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait A {
#[allow(non_camel_case_types)]
type bad;
}
fn fully_qualified<T: A>()
where
<T as A>::method(..): Send,
//~^ ERROR cannot find method or associated constant `method` in trait `A`
<T as A>::bad(..): Send,
//~^ ERROR expected method or associated constant, found associated type `A::bad`
{
}
fn type_dependent<T: A>()
where
T::method(..): Send,
//~^ associated function `method` not found for `T`
{
}
fn main() {}

View file

@ -0,0 +1,33 @@
error[E0576]: cannot find method or associated constant `method` in trait `A`
--> $DIR/path-missing.rs:11:15
|
LL | <T as A>::method(..): Send,
| ^^^^^^ not found in `A`
error[E0575]: expected method or associated constant, found associated type `A::bad`
--> $DIR/path-missing.rs:13:5
|
LL | <T as A>::bad(..): Send,
| ^^^^^^^^^^^^^^^^^
|
= note: can't use a type alias as a constructor
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-missing.rs:1:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0220]: associated function `method` not found for `T`
--> $DIR/path-missing.rs:20:8
|
LL | T::method(..): Send,
| ^^^^^^ associated function `method` not found
error: aborting due to 3 previous errors; 1 warning emitted
Some errors have detailed explanations: E0220, E0575, E0576.
For more information about an error, try `rustc --explain E0220`.

View file

@ -0,0 +1,15 @@
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Trait {
fn method() -> impl Sized;
}
fn test()
where
Trait::method(..): Send,
//~^ ERROR ambiguous associated type
{
}
fn main() {}

View file

@ -0,0 +1,23 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-no-qself.rs:1:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0223]: ambiguous associated type
--> $DIR/path-no-qself.rs:10:5
|
LL | Trait::method(..): Send,
| ^^^^^^^^^^^^^^^^^
|
help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path
|
LL | <Example as Trait>::method: Send,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0223`.

View file

@ -0,0 +1,21 @@
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Trait {
fn method() -> impl Sized;
}
struct Adt;
fn non_param_qself()
where
<()>::method(..): Send,
//~^ ERROR ambiguous associated function
i32::method(..): Send,
//~^ ERROR ambiguous associated function
Adt::method(..): Send,
//~^ ERROR ambiguous associated function
{
}
fn main() {}

View file

@ -0,0 +1,30 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-non-param-qself.rs:1:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0223]: ambiguous associated function
--> $DIR/path-non-param-qself.rs:12:5
|
LL | <()>::method(..): Send,
| ^^^^^^^^^^^^^^^^
error[E0223]: ambiguous associated function
--> $DIR/path-non-param-qself.rs:14:5
|
LL | i32::method(..): Send,
| ^^^^^^^^^^^^^^^
error[E0223]: ambiguous associated function
--> $DIR/path-non-param-qself.rs:16:5
|
LL | Adt::method(..): Send,
| ^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0223`.

View file

@ -0,0 +1,27 @@
//@ check-pass
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Foo {
fn method() -> impl Sized;
}
trait Bar: Foo {
fn other()
where
Self::method(..): Send;
}
fn is_send(_: impl Send) {}
impl<T: Foo> Bar for T {
fn other()
where
Self::method(..): Send,
{
is_send(Self::method());
}
}
fn main() {}

View file

@ -0,0 +1,11 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-self-qself.rs:3:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: 1 warning emitted

View file

@ -0,0 +1,22 @@
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Foo {
fn method<T>() -> impl Sized;
}
fn test<T: Foo>()
where
<T as Foo>::method(..): Send,
//~^ ERROR return type notation is not allowed for functions that have type parameters
{
}
fn test_type_dependent<T: Foo>()
where
<T as Foo>::method(..): Send,
//~^ ERROR return type notation is not allowed for functions that have type parameters
{
}
fn main() {}

View file

@ -0,0 +1,29 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-type-param.rs:1:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
error: return type notation is not allowed for functions that have type parameters
--> $DIR/path-type-param.rs:10:5
|
LL | fn method<T>() -> impl Sized;
| - type parameter declared here
...
LL | <T as Foo>::method(..): Send,
| ^^^^^^^^^^^^^^^^^^^^^^
error: return type notation is not allowed for functions that have type parameters
--> $DIR/path-type-param.rs:17:5
|
LL | fn method<T>() -> impl Sized;
| - type parameter declared here
...
LL | <T as Foo>::method(..): Send,
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors; 1 warning emitted

View file

@ -0,0 +1,25 @@
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Trait {
fn method() -> impl Sized;
}
struct DoesntWork;
impl Trait for DoesntWork {
fn method() -> impl Sized {
std::ptr::null_mut::<()>()
// This isn't `Send`.
}
}
fn test<T: Trait>()
where
T::method(..): Send,
{
}
fn main() {
test::<DoesntWork>();
//~^ ERROR `*mut ()` cannot be sent between threads safely
}

View file

@ -0,0 +1,36 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-unsatisfied.rs:1:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0277]: `*mut ()` cannot be sent between threads safely
--> $DIR/path-unsatisfied.rs:23:12
|
LL | fn method() -> impl Sized {
| ---------- within this `impl Sized`
...
LL | test::<DoesntWork>();
| ^^^^^^^^^^ `*mut ()` cannot be sent between threads safely
|
= help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()`, which is required by `impl Sized: Send`
note: required because it appears within the type `impl Sized`
--> $DIR/path-unsatisfied.rs:10:20
|
LL | fn method() -> impl Sized {
| ^^^^^^^^^^
note: required by a bound in `test`
--> $DIR/path-unsatisfied.rs:18:20
|
LL | fn test<T: Trait>()
| ---- required by a bound in this function
LL | where
LL | T::method(..): Send,
| ^^^^ required by this bound in `test`
error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,23 @@
//@ check-pass
#![feature(return_type_notation)]
//~^ WARN the feature `return_type_notation` is incomplete
trait Trait {
fn method() -> impl Sized;
}
struct Works;
impl Trait for Works {
fn method() -> impl Sized {}
}
fn test<T: Trait>()
where
T::method(..): Send,
{
}
fn main() {
test::<Works>();
}

View file

@ -0,0 +1,11 @@
warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/path-works.rs:3:12
|
LL | #![feature(return_type_notation)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: 1 warning emitted

View file

@ -1,8 +1,8 @@
pub fn foo(num: i32) -> i32 {
let foo: i32::from_be(num);
//~^ ERROR expected type, found local variable `num`
//~| ERROR parenthesized type parameters may only be used with a `Fn` trait
//~| ERROR ambiguous associated type
//~| ERROR argument types not allowed with return type notation
//~| ERROR return type notation not allowed in this position yet
foo
}

View file

@ -6,29 +6,22 @@ LL | let foo: i32::from_be(num);
| |
| help: use `=` if you meant to assign
error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
--> $DIR/let-binding-init-expr-as-ty.rs:2:19
error: argument types not allowed with return type notation
--> $DIR/let-binding-init-expr-as-ty.rs:2:26
|
LL | let foo: i32::from_be(num);
| ^^^^^^^^^^^^ only `Fn` traits may use parentheses
| ^^^^^ help: remove the input types: `()`
|
help: use angle brackets instead
|
LL | let foo: i32::from_be<num>;
| ~ ~
= note: see issue #109417 <https://github.com/rust-lang/rust/issues/109417> for more information
= help: add `#![feature(return_type_notation)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0223]: ambiguous associated type
error: return type notation not allowed in this position yet
--> $DIR/let-binding-init-expr-as-ty.rs:2:14
|
LL | let foo: i32::from_be(num);
| ^^^^^^^^^^^^^^^^^
|
help: if there were a trait named `Example` with associated type `from_be` implemented for `i32`, you could use the fully-qualified path
|
LL | let foo: <i32 as Example>::from_be;
| ~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0214, E0223, E0573.
For more information about an error, try `rustc --explain E0214`.
For more information about this error, try `rustc --explain E0573`.