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)] #[derive(Copy, Clone, Debug)]
enum AllowReturnTypeNotation { enum AllowReturnTypeNotation {
/// Only in types, since RTN is denied later during HIR lowering.
Yes, Yes,
/// All other positions (path expr, method, use tree).
No, No,
} }
#[derive(Copy, Clone, Debug)]
enum GenericArgsMode { enum GenericArgsMode {
ParenSugar, ParenSugar,
ReturnTypeNotation, ReturnTypeNotation,
@ -1239,7 +1240,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
qself, qself,
path, path,
param_mode, param_mode,
// We deny these after the fact in HIR->middle type lowering.
AllowReturnTypeNotation::Yes, AllowReturnTypeNotation::Yes,
itctx, itctx,
None, None,

View File

@ -16,10 +16,9 @@ use super::errors::{
GenericTypeWithParentheses, UseAngleBrackets, GenericTypeWithParentheses, UseAngleBrackets,
}; };
use super::{ use super::{
AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, LifetimeRes, AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition,
LoweringContext, ParamMode, ResolverAstLoweringExt, LifetimeRes, LoweringContext, ParamMode, ResolverAstLoweringExt,
}; };
use crate::ImplTraitPosition;
impl<'a, 'hir> LoweringContext<'a, 'hir> { impl<'a, 'hir> LoweringContext<'a, 'hir> {
#[instrument(level = "trace", skip(self))] #[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 { for predicate in hir_generics.predicates {
match predicate { match predicate {
hir::WherePredicate::BoundPredicate(bound_pred) => { 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); let bound_vars = tcx.late_bound_vars(bound_pred.hir_id);
// Keep the type around in a dummy predicate, in case of no bounds. // 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` // That way, `where Ty:` is not a complete noop (see #53696) and `Ty`
@ -770,6 +771,10 @@ impl<'tcx> ItemCtxt<'tcx> {
continue; 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 { let (only_self_bounds, assoc_name) = match filter {
PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {
(OnlySelfBounds(false), None) (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()) { let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) {
ty ty
} else if matches!(filter, PredicateFilter::All) { } else if matches!(filter, PredicateFilter::All) {
self.lower_ty(predicate.bounded_ty) self.lowerer().lower_ty_maybe_return_type_notation(predicate.bounded_ty)
} else { } else {
continue; continue;
}; };

View File

@ -889,7 +889,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
(pair, r) (pair, r)
}) })
.unzip(); .unzip();
self.record_late_bound_vars(hir_id, binders); 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 // 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 // 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 // will be `Concatenating` anyways, so we don't have to worry about the depth
@ -1839,6 +1844,110 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id); let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id);
assert_eq!(old_value, Some(bad_def)); 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 /// Detects late-bound lifetimes and inserts them into

View File

@ -6,6 +6,7 @@ use rustc_errors::struct_span_code_err;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::HirId;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
@ -15,6 +16,7 @@ use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
use smallvec::SmallVec; use smallvec::SmallVec;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use super::errors::GenericsArgsErrExtend;
use crate::bounds::Bounds; use crate::bounds::Bounds;
use crate::errors; use crate::errors;
use crate::hir_ty_lowering::{ use crate::hir_ty_lowering::{
@ -332,74 +334,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.or_insert(constraint.span); .or_insert(constraint.span);
let projection_term = if let ty::AssocKind::Fn = assoc_kind { 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); 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 { } else {
// Create the generic arguments for the associated type or constant by joining the // 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 // parent arguments (the arguments of the trait) and the own arguments (the ones of
@ -525,6 +464,219 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} }
Ok(()) 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. /// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings.

View File

@ -820,10 +820,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
/// ///
/// `ty_param_def_id` is the `LocalDefId` of the type parameter. /// `ty_param_def_id` is the `LocalDefId` of the type parameter.
#[instrument(level = "debug", skip_all, ret)] #[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, &self,
ty_param_def_id: LocalDefId, ty_param_def_id: LocalDefId,
ty_param_span: Span, ty_param_span: Span,
kind: ty::AssocKind,
assoc_name: Ident, assoc_name: Ident,
span: Span, span: Span,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> { ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
@ -841,7 +842,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
}, },
AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span), AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span),
ty::AssocKind::Type, kind,
assoc_name, assoc_name,
span, span,
None, None,
@ -1081,9 +1082,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
( (
&ty::Param(_), &ty::Param(_),
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), 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(), param_did.expect_local(),
qself.span, qself.span,
ty::AssocKind::Type,
assoc_ident, assoc_ident,
span, span,
)?, )?,
@ -1545,48 +1547,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
debug!(?trait_def_id); debug!(?trait_def_id);
let Some(self_ty) = opt_self_ty else { let Some(self_ty) = opt_self_ty else {
let path_str = tcx.def_path_str(trait_def_id); return self.error_missing_qpath_self_ty(trait_def_id, span, item_segment);
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);
}; };
debug!(?self_ty); debug!(?self_ty);
@ -1600,6 +1561,53 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Ty::new_projection_from_args(tcx, item_def_id, item_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();
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>( pub fn prohibit_generic_args<'a>(
&self, &self,
segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone, segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
@ -1910,19 +1918,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::BoundConstness::NotConst, 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) => { Res::PrimTy(prim_ty) => {
assert_eq!(opt_self_ty, None); assert_eq!(opt_self_ty, None);
let _ = self.prohibit_generic_args( let _ = self.prohibit_generic_args(
@ -1943,7 +1938,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.tcx() .tcx()
.dcx() .dcx()
.span_delayed_bug(path.span, "path with `Res::Err` but no error emitted"); .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(..) => { Res::Def(..) => {
assert_eq!( assert_eq!(
@ -2098,6 +2093,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), 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)) => { hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
debug!(?qself, ?segment); debug!(?qself, ?segment);
let ty = self.lower_ty(qself); let ty = self.lower_ty(qself);

View File

@ -790,6 +790,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
TyKind::Path(qself, path) => { TyKind::Path(qself, path) => {
self.diag_metadata.current_type_path = Some(ty); self.diag_metadata.current_type_path = Some(ty);
// TODO:
let source = if let Some(seg) = path.segments.last() let source = if let Some(seg) = path.segments.last()
&& let Some(args) = &seg.args && let Some(args) = &seg.args
&& matches!(**args, GenericArgs::ParenthesizedElided(..)) && matches!(**args, GenericArgs::ParenthesizedElided(..))

View File

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

View File

@ -8,40 +8,16 @@ LL | #![feature(return_type_notation)]
= note: `#[warn(incomplete_features)]` on by default = note: `#[warn(incomplete_features)]` on by default
error: return type notation not allowed in this position yet 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::(..); 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 error: return type notation not allowed in this position yet
--> $DIR/bare-path.rs:14:5 --> $DIR/bare-path.rs:17:12
|
LL | <T as Tr>::method(..): Send,
| ^^^^^^^^^^^^^^^^^^^^^
error: expected type, found function
--> $DIR/bare-path.rs:19:15
| |
LL | let _: T::method(..); 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); let foo: i32::from_be(num);
//~^ ERROR expected type, found local variable `num` //~^ ERROR expected type, found local variable `num`
//~| ERROR argument types not allowed with return type notation //~| ERROR argument types not allowed with return type notation
//~| ERROR ambiguous associated type //~| ERROR return type notation not allowed in this position yet
foo 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 = 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 = 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 --> $DIR/let-binding-init-expr-as-ty.rs:2:14
| |
LL | let foo: i32::from_be(num); 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 error: aborting due to 3 previous errors
Some errors have detailed explanations: E0223, E0573. For more information about this error, try `rustc --explain E0573`.
For more information about an error, try `rustc --explain E0223`.