Implement RTN in resolve_bound_vars and HIR ty lowering

This commit is contained in:
Michael Goulet 2024-08-26 15:03:30 -04:00
parent 19881b5a5a
commit 51b51bb570
11 changed files with 411 additions and 177 deletions

View File

@ -487,11 +487,12 @@ enum ParamMode {
#[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,
}
#[derive(Copy, Clone, Debug)]
enum GenericArgsMode {
ParenSugar,
ReturnTypeNotation,
@ -1239,7 +1240,6 @@ fn lower_path_ty(
qself,
path,
param_mode,
// We deny these after the fact in HIR->middle type lowering.
AllowReturnTypeNotation::Yes,
itctx,
None,

View File

@ -16,10 +16,9 @@
GenericTypeWithParentheses, UseAngleBrackets,
};
use super::{
AllowReturnTypeNotation, 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))]

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 @@ fn probe_ty_param_bounds_in_generics(
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 @@ fn probe_ty_param_bounds_in_generics(
}
};
// 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 @@ fn visit_where_predicate(&mut self, predicate: &'tcx hir::WherePredicate<'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
@ -1839,6 +1844,110 @@ fn uninsert_lifetime_on_error(
let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id);
assert_eq!(old_value, Some(bad_def));
}
// TODO:
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 {
// TODO:
return;
};
let (mut bound_vars, item_def_id, item_segment) = match qpath {
// TODO:
hir::QPath::Resolved(_, path)
if let [.., item_segment] = &path.segments[..]
&& item_segment.args.is_some_and(|args| {
matches!(
args.parenthesized,
hir::GenericArgsParentheses::ReturnTypeNotation
)
}) =>
{
let Res::Def(DefKind::AssocFn, item_def_id) = path.res else {
bug!();
};
(vec![], item_def_id, item_segment)
}
// TODO:
hir::QPath::TypeRelative(qself, item_segment)
if item_segment.args.is_some_and(|args| {
matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
}) =>
{
let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind else {
return;
};
match path.res {
Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { trait_: _ } => {
let Some(generics) = self.tcx.hir_owner_node(hir_id.owner).generics()
else {
return;
};
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)
}
Res::SelfTyAlias { is_trait_impl: true, .. } => todo!(),
_ => return,
}
}
_ => return,
};
// TODO:
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());
// TODO:
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);
// TODO: subtle
self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved);
}
}
/// Detects late-bound lifetimes and inserts them into

View File

@ -6,6 +6,7 @@
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 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 @@ pub(super) fn lower_assoc_item_constraint(
.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,219 @@ pub(super) fn lower_assoc_item_constraint(
}
Ok(())
}
// TODO:
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
)
}) =>
{
let _ =
self.prohibit_generic_args(mod_segments.iter(), GenericsArgsErrExtend::None);
let Res::Def(DefKind::AssocFn, item_def_id) = path.res else {
bug!();
};
let trait_def_id = tcx.parent(item_def_id);
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,
);
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),
}
}
// TODO:
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,
)?,
_ => todo!(),
};
// Don't let `T::method` resolve to some `for<'a> <T as Tr<'a>>::method`.
// 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`.
if bound.has_bound_vars() {
todo!();
}
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))
}
// TODO:
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

@ -820,10 +820,11 @@ fn lower_path_segment(
///
/// `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 +842,7 @@ fn probe_single_ty_param_bound_for_assoc_ty(
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 +1082,10 @@ pub fn lower_assoc_path(
(
&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,6 +1547,27 @@ fn lower_qpath(
debug!(?trait_def_id);
let Some(self_ty) = opt_self_ty else {
return self.error_missing_qpath_self_ty(trait_def_id, span, item_segment);
};
debug!(?self_ty);
let trait_ref =
self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false, constness);
debug!(?trait_ref);
let item_args =
self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, trait_ref.args);
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();
@ -1580,24 +1603,9 @@ fn lower_qpath(
// 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);
};
debug!(?self_ty);
let trait_ref =
self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false, constness);
debug!(?trait_ref);
let item_args =
self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, trait_ref.args);
Ty::new_projection_from_args(tcx, item_def_id, item_args)
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>(
@ -1910,19 +1918,6 @@ pub fn lower_path(
ty::BoundConstness::NotConst,
)
}
// Deny any qpath types that were successfully lowered in AST lowering.
Res::Def(DefKind::AssocFn, _)
if let [.., _trait_segment, item_segment] = &path.segments[..]
&& item_segment.args.is_some_and(|args| {
matches!(
args.parenthesized,
hir::GenericArgsParentheses::ReturnTypeNotation
)
}) =>
{
let guar = self.dcx().emit_err(BadReturnTypeNotation { span: path.span });
Ty::new_error(tcx, guar)
}
Res::PrimTy(prim_ty) => {
assert_eq!(opt_self_ty, None);
let _ = self.prohibit_generic_args(
@ -1943,7 +1938,7 @@ pub fn lower_path(
.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!(
@ -2098,6 +2093,15 @@ pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
}
}
// TODO:
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

@ -790,6 +790,7 @@ fn visit_ty(&mut self, ty: &'ast Ty) {
TyKind::Path(qself, path) => {
self.diag_metadata.current_type_path = Some(ty);
// TODO:
let source = if let Some(seg) = path.segments.last()
&& let Some(args) = &seg.args
&& matches!(**args, GenericArgs::ParenthesizedElided(..))

View File

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

View File

@ -8,40 +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:17:23
--> $DIR/bare-path.rs:15:23
|
LL | let _ = T::CONST::(..);
| ^^^^
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: return type notation not allowed in this position yet
--> $DIR/bare-path.rs:14:5
|
LL | <T as Tr>::method(..): Send,
| ^^^^^^^^^^^^^^^^^^^^^
error: expected type, found function
--> $DIR/bare-path.rs:19:15
--> $DIR/bare-path.rs:17:12
|
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 4 previous errors; 1 warning emitted
error: aborting due to 2 previous errors; 1 warning emitted

View File

@ -2,7 +2,7 @@ pub fn foo(num: i32) -> i32 {
let foo: i32::from_be(num);
//~^ ERROR expected type, found local variable `num`
//~| ERROR argument types not allowed with return type notation
//~| ERROR ambiguous associated type
//~| ERROR return type notation not allowed in this position yet
foo
}

View File

@ -16,18 +16,12 @@ LL | let foo: i32::from_be(num);
= 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: E0223, E0573.
For more information about an error, try `rustc --explain E0223`.
For more information about this error, try `rustc --explain E0573`.