1use std::sync::Arc;23use rustc_ast::{self as ast, *};4use rustc_errors::StashKey;5use rustc_hir::def::{DefKind, PartialRes, PerNS, Res};6use rustc_hir::def_id::DefId;7use rustc_hir::{self as hir, GenericArg};8use rustc_middle::{span_bug, ty};9use rustc_session::errors::add_feature_diagnostics;10use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};11use smallvec::smallvec;12use tracing::{debug, instrument};1314use super::errors::{15 AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, BadReturnTypeNotation,16 GenericTypeWithParentheses, RTNSuggestion, UseAngleBrackets,17};18use super::{19 AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition,20 LifetimeRes, LoweringContext, ParamMode, ResolverAstLoweringExt,21};2223impl<'hir> LoweringContext<'_, 'hir> {24 #[instrument(level = "trace", skip(self))]25 pub(crate) fn lower_qpath(26 &mut self,27 id: NodeId,28 qself: &Option<Box<QSelf>>,29 p: &Path,30 param_mode: ParamMode,31 allow_return_type_notation: AllowReturnTypeNotation,32 itctx: ImplTraitContext,33 // modifiers of the impl/bound if this is a trait path34 modifiers: Option<ast::TraitBoundModifiers>,35 ) -> hir::QPath<'hir> {36 let qself_position = qself.as_ref().map(|q| q.position);37 let qself = qself38 .as_ref()39 // Reject cases like `<impl Trait>::Assoc` and `<impl Trait as Trait>::Assoc`.40 .map(|q| {41 self.lower_ty_alloc(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path))42 });4344 let partial_res = self.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));45 let base_res = partial_res.base_res();46 let unresolved_segments = partial_res.unresolved_segments();4748 let mut res = self.lower_res(base_res);4950 // When we have an `async` kw on a bound, map the trait it resolves to.51 if let Some(TraitBoundModifiers { asyncness: BoundAsyncness::Async(_), .. }) = modifiers {52 match res {53 Res::Def(DefKind::Trait, def_id) => {54 if let Some(async_def_id) = self.map_trait_to_async_trait(def_id) {55 res = Res::Def(DefKind::Trait, async_def_id);56 } else {57 self.dcx().emit_err(AsyncBoundOnlyForFnTraits { span: p.span });58 }59 }60 Res::Err => {61 // No additional error.62 }63 _ => {64 // This error isn't actually emitted AFAICT, but it's best to keep65 // it around in case the resolver doesn't always check the defkind66 // of an item or something.67 self.dcx().emit_err(AsyncBoundNotOnTrait { span: p.span, descr: res.descr() });68 }69 }70 }7172 // Ungate the `async_fn_traits` feature in the path if the trait is73 // named via either `async Fn*()` or `AsyncFn*()`.74 let bound_modifier_allowed_features = if let Res::Def(DefKind::Trait, async_def_id) = res75 && self.tcx.async_fn_trait_kind_from_def_id(async_def_id).is_some()76 {77 Some(Arc::clone(&self.allow_async_fn_traits))78 } else {79 None80 };8182 // Only permit `impl Trait` in the final segment. E.g., we permit `Option<impl Trait>`,83 // `option::Option<T>::Xyz<impl Trait>` and reject `option::Option<impl Trait>::Xyz`.84 let itctx = |i| {85 if i + 1 == p.segments.len() {86 itctx87 } else {88 ImplTraitContext::Disallowed(ImplTraitPosition::Path)89 }90 };9192 let path_span_lo = p.span.shrink_to_lo();93 let proj_start = p.segments.len() - unresolved_segments;94 let path = self.arena.alloc(hir::Path {95 res,96 segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map(97 |(i, segment)| {98 let param_mode = match (qself_position, param_mode) {99 (Some(j), ParamMode::Optional) if i < j => {100 // This segment is part of the trait path in a101 // qualified path - one of `a`, `b` or `Trait`102 // in `<X as a::b::Trait>::T::U::method`.103 ParamMode::Explicit104 }105 _ => param_mode,106 };107108 let generic_args_mode = match base_res {109 // `a::b::Trait(Args)`110 Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {111 GenericArgsMode::ParenSugar112 }113 // `a::b::Trait(Args)::TraitItem`114 Res::Def(DefKind::AssocFn, _)115 | Res::Def(DefKind::AssocConst { .. }, _)116 | Res::Def(DefKind::AssocTy, _)117 if i + 2 == proj_start =>118 {119 GenericArgsMode::ParenSugar120 }121 Res::Def(DefKind::AssocFn, _) if i + 1 == proj_start => {122 match allow_return_type_notation {123 AllowReturnTypeNotation::Yes => GenericArgsMode::ReturnTypeNotation,124 AllowReturnTypeNotation::No => GenericArgsMode::Err,125 }126 }127 // Avoid duplicated errors.128 Res::Err => GenericArgsMode::Silence,129 // An error130 _ => GenericArgsMode::Err,131 };132133 self.lower_path_segment(134 p.span,135 segment,136 param_mode,137 generic_args_mode,138 itctx(i),139 bound_modifier_allowed_features.clone(),140 )141 },142 )),143 span: self.lower_span(144 p.segments[..proj_start]145 .last()146 .map_or(path_span_lo, |segment| path_span_lo.to(segment.span())),147 ),148 });149150 if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features {151 path.span = self.mark_span_with_reason(152 DesugaringKind::BoundModifier,153 path.span,154 Some(bound_modifier_allowed_features),155 );156 }157158 // Simple case, either no projections, or only fully-qualified.159 // E.g., `std::mem::size_of` or `<I as Iterator>::Item`.160 if unresolved_segments == 0 {161 return hir::QPath::Resolved(qself, path);162 }163164 // Create the innermost type that we're projecting from.165 let mut ty = if path.segments.is_empty() {166 // If the base path is empty that means there exists a167 // syntactical `Self`, e.g., `&i32` in `<&i32>::clone`.168 qself.expect("missing QSelf for <T>::...")169 } else {170 // Otherwise, the base path is an implicit `Self` type path,171 // e.g., `Vec` in `Vec::new` or `<I as Iterator>::Item` in172 // `<I as Iterator>::Item::default`.173 let new_id = self.next_id();174 self.arena.alloc(self.ty_path(new_id, path.span, hir::QPath::Resolved(qself, path)))175 };176177 // Anything after the base path are associated "extensions",178 // out of which all but the last one are associated types,179 // e.g., for `std::vec::Vec::<T>::IntoIter::Item::clone`:180 // * base path is `std::vec::Vec<T>`181 // * "extensions" are `IntoIter`, `Item` and `clone`182 // * type nodes are:183 // 1. `std::vec::Vec<T>` (created above)184 // 2. `<std::vec::Vec<T>>::IntoIter`185 // 3. `<<std::vec::Vec<T>>::IntoIter>::Item`186 // * final path is `<<<std::vec::Vec<T>>::IntoIter>::Item>::clone`187 for (i, segment) in p.segments.iter().enumerate().skip(proj_start) {188 // If this is a type-dependent `T::method(..)`.189 let generic_args_mode = if i + 1 == p.segments.len()190 && matches!(allow_return_type_notation, AllowReturnTypeNotation::Yes)191 {192 GenericArgsMode::ReturnTypeNotation193 } else {194 GenericArgsMode::Err195 };196197 let hir_segment = self.arena.alloc(self.lower_path_segment(198 p.span,199 segment,200 param_mode,201 generic_args_mode,202 itctx(i),203 None,204 ));205 let qpath = hir::QPath::TypeRelative(ty, hir_segment);206207 // It's finished, return the extension of the right node type.208 if i == p.segments.len() - 1 {209 return qpath;210 }211212 // Wrap the associated extension in another type node.213 let new_id = self.next_id();214 ty = self.arena.alloc(self.ty_path(new_id, path_span_lo.to(segment.span()), qpath));215 }216217 // We should've returned in the for loop above.218219 self.dcx().span_bug(220 p.span,221 format!(222 "lower_qpath: no final extension segment in {}..{}",223 proj_start,224 p.segments.len()225 ),226 );227 }228229 pub(crate) fn lower_use_path(230 &mut self,231 res: PerNS<Option<Res>>,232 p: &Path,233 param_mode: ParamMode,234 ) -> &'hir hir::UsePath<'hir> {235 assert!(!res.is_empty());236 self.arena.alloc(hir::UsePath {237 res,238 segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {239 self.lower_path_segment(240 p.span,241 segment,242 param_mode,243 GenericArgsMode::Err,244 ImplTraitContext::Disallowed(ImplTraitPosition::Path),245 None,246 )247 })),248 span: self.lower_span(p.span),249 })250 }251252 pub(crate) fn lower_path_segment(253 &mut self,254 path_span: Span,255 segment: &PathSegment,256 param_mode: ParamMode,257 generic_args_mode: GenericArgsMode,258 itctx: ImplTraitContext,259 // Additional features ungated with a bound modifier like `async`.260 // This is passed down to the implicit associated type binding in261 // parenthesized bounds.262 bound_modifier_allowed_features: Option<Arc<[Symbol]>>,263 ) -> hir::PathSegment<'hir> {264 debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment);265 let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() {266 match generic_args {267 GenericArgs::AngleBracketed(data) => {268 self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)269 }270 GenericArgs::Parenthesized(data) => match generic_args_mode {271 GenericArgsMode::ReturnTypeNotation => {272 let err = match (&data.inputs[..], &data.output) {273 ([_, ..], FnRetTy::Default(_)) => {274 BadReturnTypeNotation::Inputs { span: data.inputs_span }275 }276 ([], FnRetTy::Default(_)) => {277 BadReturnTypeNotation::NeedsDots { span: data.inputs_span }278 }279 // The case `T: Trait<method(..) -> Ret>` is handled in the parser.280 (_, FnRetTy::Ty(ty)) => {281 let span = data.inputs_span.shrink_to_hi().to(ty.span);282 BadReturnTypeNotation::Output {283 span,284 suggestion: RTNSuggestion {285 output: span,286 input: data.inputs_span,287 },288 }289 }290 };291 let mut err = self.dcx().create_err(err);292 if !self.tcx.features().return_type_notation()293 && self.tcx.sess.is_nightly_build()294 {295 add_feature_diagnostics(296 &mut err,297 &self.tcx.sess,298 sym::return_type_notation,299 );300 }301 err.stash(path_span, StashKey::ReturnTypeNotation);302 (303 GenericArgsCtor {304 args: Default::default(),305 constraints: &[],306 parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,307 span: path_span,308 },309 false,310 )311 }312 GenericArgsMode::ParenSugar | GenericArgsMode::Silence => self313 .lower_parenthesized_parameter_data(314 data,315 itctx,316 bound_modifier_allowed_features,317 ),318 GenericArgsMode::Err => {319 // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`320 let sub = if !data.inputs.is_empty() {321 // Start of the span to the 1st character of 1st argument322 let open_param = data.inputs_span.shrink_to_lo().to(data323 .inputs324 .first()325 .unwrap()326 .span327 .shrink_to_lo());328 // Last character position of last argument to the end of the span329 let close_param = data330 .inputs331 .last()332 .unwrap()333 .span334 .shrink_to_hi()335 .to(data.inputs_span.shrink_to_hi());336337 Some(UseAngleBrackets { open_param, close_param })338 } else {339 None340 };341 self.dcx().emit_err(GenericTypeWithParentheses { span: data.span, sub });342 (343 self.lower_angle_bracketed_parameter_data(344 &data.as_angle_bracketed_args(),345 param_mode,346 itctx,347 )348 .0,349 false,350 )351 }352 },353 GenericArgs::ParenthesizedElided(span) => {354 match generic_args_mode {355 GenericArgsMode::ReturnTypeNotation | GenericArgsMode::Silence => {356 // Ok357 }358 GenericArgsMode::ParenSugar | GenericArgsMode::Err => {359 self.dcx().emit_err(BadReturnTypeNotation::Position { span: *span });360 }361 }362 (363 GenericArgsCtor {364 args: Default::default(),365 constraints: &[],366 parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,367 span: *span,368 },369 false,370 )371 }372 }373 } else {374 (375 GenericArgsCtor {376 args: Default::default(),377 constraints: &[],378 parenthesized: hir::GenericArgsParentheses::No,379 span: path_span.shrink_to_hi(),380 },381 param_mode == ParamMode::Optional,382 )383 };384385 let has_lifetimes =386 generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));387388 // FIXME(return_type_notation): Is this correct? I think so.389 if generic_args.parenthesized != hir::GenericArgsParentheses::ParenSugar && !has_lifetimes {390 self.maybe_insert_elided_lifetimes_in_path(391 path_span,392 segment.id,393 segment.ident.span,394 &mut generic_args,395 );396 }397398 let res = self.expect_full_res(segment.id);399 let hir_id = self.lower_node_id(segment.id);400 debug!(401 "lower_path_segment: ident={:?} original-id={:?} new-id={:?}",402 segment.ident, segment.id, hir_id,403 );404405 hir::PathSegment {406 ident: self.lower_ident(segment.ident),407 hir_id,408 res: self.lower_res(res),409 infer_args,410 args: if generic_args.is_empty() && generic_args.span.is_empty() {411 None412 } else {413 Some(generic_args.into_generic_args(self))414 },415 }416 }417418 fn maybe_insert_elided_lifetimes_in_path(419 &mut self,420 path_span: Span,421 segment_id: NodeId,422 segment_ident_span: Span,423 generic_args: &mut GenericArgsCtor<'hir>,424 ) {425 let (start, end) = match self.resolver.get_lifetime_res(segment_id) {426 Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end),427 None => return,428 Some(res) => {429 span_bug!(path_span, "expected an elided lifetime to insert. found {res:?}")430 }431 };432 let expected_lifetimes = end.as_usize() - start.as_usize();433 debug!(expected_lifetimes);434435 // Note: these spans are used for diagnostics when they can't be inferred.436 // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label437 let (elided_lifetime_span, angle_brackets) = if generic_args.span.is_empty() {438 // No brackets, e.g. `Path`: use an empty span just past the end of the identifier.439 // HACK: we use find_ancestor_inside to properly suggest elided spans in paths440 // originating from macros, since the segment's span might be from a macro arg.441 (442 segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span),443 hir::AngleBrackets::Missing,444 )445 } else {446 // Brackets, e.g. `Path<>` or `Path<T>`: use an empty span just after the `<`.447 (448 generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo(),449 if generic_args.is_empty() {450 hir::AngleBrackets::Empty451 } else {452 hir::AngleBrackets::Full453 },454 )455 };456457 generic_args.args.insert_many(458 0,459 (start..end).map(|id| {460 let l =461 self.lower_lifetime_hidden_in_path(id, elided_lifetime_span, angle_brackets);462 GenericArg::Lifetime(l)463 }),464 );465 }466467 pub(crate) fn lower_angle_bracketed_parameter_data(468 &mut self,469 data: &AngleBracketedArgs,470 param_mode: ParamMode,471 itctx: ImplTraitContext,472 ) -> (GenericArgsCtor<'hir>, bool) {473 let has_non_lt_args = data.args.iter().any(|arg| match arg {474 AngleBracketedArg::Arg(ast::GenericArg::Lifetime(_))475 | AngleBracketedArg::Constraint(_) => false,476 AngleBracketedArg::Arg(ast::GenericArg::Type(_) | ast::GenericArg::Const(_)) => true,477 });478 let args = data479 .args480 .iter()481 .filter_map(|arg| match arg {482 AngleBracketedArg::Arg(arg) => Some(self.lower_generic_arg(arg, itctx)),483 AngleBracketedArg::Constraint(_) => None,484 })485 .collect();486 let constraints =487 self.arena.alloc_from_iter(data.args.iter().filter_map(|arg| match arg {488 AngleBracketedArg::Constraint(c) => {489 Some(self.lower_assoc_item_constraint(c, itctx))490 }491 AngleBracketedArg::Arg(_) => None,492 }));493 let ctor = GenericArgsCtor {494 args,495 constraints,496 parenthesized: hir::GenericArgsParentheses::No,497 span: data.span,498 };499 (ctor, !has_non_lt_args && param_mode == ParamMode::Optional)500 }501502 fn lower_parenthesized_parameter_data(503 &mut self,504 data: &ParenthesizedArgs,505 itctx: ImplTraitContext,506 bound_modifier_allowed_features: Option<Arc<[Symbol]>>,507 ) -> (GenericArgsCtor<'hir>, bool) {508 // Switch to `PassThrough` mode for anonymous lifetimes; this509 // means that we permit things like `&Ref<T>`, where `Ref` has510 // a hidden lifetime parameter. This is needed for backwards511 // compatibility, even in contexts like an impl header where512 // we generally don't permit such things (see #51008).513 let ParenthesizedArgs { span, inputs, inputs_span, output } = data;514 let inputs = self.arena.alloc_from_iter(inputs.iter().map(|ty| {515 self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam))516 }));517 let output_ty = match output {518 // Only allow `impl Trait` in return position. i.e.:519 // ```rust520 // fn f(_: impl Fn() -> impl Debug) -> impl Fn() -> impl Debug521 // // disallowed --^^^^^^^^^^ allowed --^^^^^^^^^^522 // ```523 FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::OpaqueTy { .. }) => {524 if self.tcx.features().impl_trait_in_fn_trait_return() {525 self.lower_ty_alloc(ty, itctx)526 } else {527 self.lower_ty_alloc(528 ty,529 ImplTraitContext::FeatureGated(530 ImplTraitPosition::FnTraitReturn,531 sym::impl_trait_in_fn_trait_return,532 ),533 )534 }535 }536 FnRetTy::Ty(ty) => self537 .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)),538 FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),539 };540 let args = smallvec![GenericArg::Type(541 self.arena.alloc(self.ty_tup(*inputs_span, inputs)).try_as_ambig_ty().unwrap()542 )];543544 // If we have a bound like `async Fn() -> T`, make sure that we mark the545 // `Output = T` associated type bound with the right feature gates.546 let mut output_span = output_ty.span;547 if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features {548 output_span = self.mark_span_with_reason(549 DesugaringKind::BoundModifier,550 output_span,551 Some(bound_modifier_allowed_features),552 );553 }554 let constraint = self.assoc_ty_binding(sym::Output, output_span, output_ty);555556 (557 GenericArgsCtor {558 args,559 constraints: arena_vec![self; constraint],560 parenthesized: hir::GenericArgsParentheses::ParenSugar,561 span: data.inputs_span,562 },563 false,564 )565 }566567 /// An associated type binding (i.e., associated type equality constraint).568 pub(crate) fn assoc_ty_binding(569 &mut self,570 assoc_ty_name: rustc_span::Symbol,571 span: Span,572 ty: &'hir hir::Ty<'hir>,573 ) -> hir::AssocItemConstraint<'hir> {574 let ident = Ident::with_dummy_span(assoc_ty_name);575 let kind = hir::AssocItemConstraintKind::Equality { term: ty.into() };576 let args = arena_vec![self;];577 let constraints = arena_vec![self;];578 let gen_args = self.arena.alloc(hir::GenericArgs {579 args,580 constraints,581 parenthesized: hir::GenericArgsParentheses::No,582 span_ext: DUMMY_SP,583 });584 hir::AssocItemConstraint {585 hir_id: self.next_id(),586 gen_args,587 span: self.lower_span(span),588 ident,589 kind,590 }591 }592593 /// When a bound is annotated with `async`, it signals to lowering that the trait594 /// that the bound refers to should be mapped to the "async" flavor of the trait.595 ///596 /// This only needs to be done until we unify `AsyncFn` and `Fn` traits into one597 /// that is generic over `async`ness, if that's ever possible, or modify the598 /// lowering of `async Fn()` bounds to desugar to another trait like `LendingFn`.599 fn map_trait_to_async_trait(&self, def_id: DefId) -> Option<DefId> {600 let lang_items = self.tcx.lang_items();601 match self.tcx.fn_trait_kind_from_def_id(def_id)? {602 ty::ClosureKind::Fn => lang_items.async_fn_trait(),603 ty::ClosureKind::FnMut => lang_items.async_fn_mut_trait(),604 ty::ClosureKind::FnOnce => lang_items.async_fn_once_trait(),605 }606 }607}