1use core::ops::ControlFlow;
2
3use rustc_errors::{Applicability, StashKey, Suggestions};
4use rustc_hir::def_id::{DefId, LocalDefId};
5use rustc_hir::intravisit::VisitorExt;
6use rustc_hir::{self as hir, AmbigArg, HirId};
7use rustc_middle::ty::print::{with_forced_trimmed_paths, with_types_for_suggestion};
8use rustc_middle::ty::util::IntTypeExt;
9use rustc_middle::ty::{self, DefiningScopeKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
10use rustc_middle::{bug, span_bug};
11use rustc_span::{DUMMY_SP, Ident, Span};
12use tracing::instrument;
13
14use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder};
15use crate::check::wfcheck::check_static_item;
16use crate::hir_ty_lowering::HirTyLowerer;
17
18mod opaque;
19
20x;#[instrument(level = "debug", skip(tcx), ret)]
21pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, Ty<'_>> {
22 use rustc_hir::*;
23 use rustc_middle::ty::Ty;
24
25 match tcx.opt_rpitit_info(def_id.to_def_id()) {
29 Some(ty::ImplTraitInTraitData::Impl { fn_def_id }) => {
30 match tcx.collect_return_position_impl_trait_in_trait_tys(fn_def_id) {
31 Ok(map) => {
32 let trait_item_def_id = tcx.trait_item_of(def_id).unwrap();
33 return map[&trait_item_def_id];
34 }
35 Err(_) => {
36 return ty::EarlyBinder::bind(
37 tcx,
38 Ty::new_error_with_message(
39 tcx,
40 DUMMY_SP,
41 "Could not collect return position impl trait in trait tys",
42 ),
43 );
44 }
45 }
46 }
47 Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
49 return ty::EarlyBinder::bind(
50 tcx,
51 Ty::new_opaque(
52 tcx,
53 ty::IsRigid::No,
54 opaque_def_id,
55 ty::GenericArgs::identity_for_item(tcx, opaque_def_id),
56 ),
57 );
58 }
59 None => {}
60 }
61
62 let hir_id = tcx.local_def_id_to_hir_id(def_id);
63
64 let icx = ItemCtxt::new(tcx, def_id);
65
66 let new_bound_fn_def = |hir: HirId, did| {
67 let args = ty::GenericArgs::identity_for_item(tcx, def_id);
68 Ty::new_fn_def(
69 tcx,
70 did,
71 match &tcx
72 .late_bound_vars_map(hir.owner)
73 .get(&hir.local_id)
74 .cloned()
75 .map(|x| tcx.mk_bound_variable_kinds(&x))
76 {
77 Some(late_bound) => ty::Binder::bind_with_vars(args, late_bound),
78 None => ty::Binder::dummy(args),
79 },
80 )
81 };
82
83 let output = match tcx.hir_node(hir_id) {
84 Node::TraitItem(item) => match item.kind {
85 TraitItemKind::Fn(_, _) => new_bound_fn_def(item.hir_id(), def_id.to_def_id()),
86 TraitItemKind::Const(ty, rhs) => rhs
87 .and_then(|rhs| {
88 ty.is_suggestable_infer_ty().then(|| {
89 infer_placeholder_type(
90 icx.lowerer(),
91 def_id,
92 rhs.hir_id(),
93 ty.span,
94 rhs.span(tcx),
95 item.ident,
96 "associated constant",
97 )
98 })
99 })
100 .unwrap_or_else(|| icx.lower_ty(ty)),
101 TraitItemKind::Type(_, Some(ty)) => icx.lower_ty(ty),
102 TraitItemKind::Type(_, None) => {
103 span_bug!(item.span, "associated type missing default");
104 }
105 },
106
107 Node::ImplItem(item) => match item.kind {
108 ImplItemKind::Fn(_, _) => new_bound_fn_def(item.hir_id(), def_id.to_def_id()),
109 ImplItemKind::Const(ty, rhs) => {
110 if ty.is_suggestable_infer_ty() {
111 infer_placeholder_type(
112 icx.lowerer(),
113 def_id,
114 rhs.hir_id(),
115 ty.span,
116 rhs.span(tcx),
117 item.ident,
118 "associated constant",
119 )
120 } else {
121 icx.lower_ty(ty)
122 }
123 }
124 ImplItemKind::Type(ty) => {
125 if let ImplItemImplKind::Inherent { .. } = item.impl_kind {
126 check_feature_inherent_assoc_ty(tcx, item.span);
127 }
128
129 icx.lower_ty(ty)
130 }
131 },
132
133 Node::Item(item) => match item.kind {
134 ItemKind::Static(_, ident, ty, body_id) => {
135 if ty.is_suggestable_infer_ty() {
136 infer_placeholder_type(
137 icx.lowerer(),
138 def_id,
139 body_id.hir_id,
140 ty.span,
141 tcx.hir_body(body_id).value.span,
142 ident,
143 "static variable",
144 )
145 } else {
146 let ty = icx.lower_ty(ty);
147 match check_static_item(tcx, def_id, ty, false) {
152 Ok(()) => ty,
153 Err(guar) => Ty::new_error(tcx, guar),
154 }
155 }
156 }
157 ItemKind::Const(ident, _, ty, rhs) => {
158 if ty.is_suggestable_infer_ty() {
159 infer_placeholder_type(
160 icx.lowerer(),
161 def_id,
162 rhs.hir_id(),
163 ty.span,
164 rhs.span(tcx),
165 ident,
166 "constant",
167 )
168 } else {
169 icx.lower_ty(ty)
170 }
171 }
172 ItemKind::TyAlias(_, _, self_ty) => icx.lower_ty(self_ty),
173 ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() {
174 spans if spans.len() > 0 => {
175 let guar = tcx.dcx().emit_err(crate::diagnostics::SelfInImplSelf {
176 span: spans.into(),
177 note: (),
178 });
179 Ty::new_error(tcx, guar)
180 }
181 _ => icx.lower_ty(self_ty),
182 },
183 ItemKind::Fn { .. } => new_bound_fn_def(item.hir_id(), def_id.to_def_id()),
184 ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
185 let def = tcx.adt_def(def_id);
186 let args = ty::GenericArgs::identity_for_item(tcx, def_id);
187 Ty::new_adt(tcx, def, args)
188 }
189 ItemKind::GlobalAsm { .. } => tcx.typeck(def_id).node_type(hir_id),
190 ItemKind::Trait { .. }
191 | ItemKind::TraitAlias(..)
192 | ItemKind::Macro(..)
193 | ItemKind::Mod(..)
194 | ItemKind::ForeignMod { .. }
195 | ItemKind::ExternCrate(..)
196 | ItemKind::Use(..) => {
197 span_bug!(item.span, "compute_type_of_item: unexpected item type: {:?}", item.kind);
198 }
199 },
200
201 Node::OpaqueTy(..) => tcx.type_of_opaque(def_id).instantiate_identity().skip_norm_wip(),
202
203 Node::ForeignItem(foreign_item) => match foreign_item.kind {
204 ForeignItemKind::Fn(_, _, _generics) => {
205 new_bound_fn_def(foreign_item.hir_id(), def_id.to_def_id())
206 }
207 ForeignItemKind::Static(ty, _, _) => {
208 let ty = icx.lower_ty(ty);
209 match check_static_item(tcx, def_id, ty, false) {
214 Ok(()) => ty,
215 Err(guar) => Ty::new_error(tcx, guar),
216 }
217 }
218 ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()),
219 },
220
221 Node::Ctor(def) | Node::Variant(Variant { data: def, .. }) => match def {
222 VariantData::Unit(..) | VariantData::Struct { .. } => {
223 tcx.type_of(tcx.hir_get_parent_item(hir_id)).instantiate_identity().skip_norm_wip()
224 }
225 VariantData::Tuple(_, hir_id, ctor) => new_bound_fn_def(*hir_id, ctor.to_def_id()),
226 },
227
228 Node::Field(field) => icx.lower_ty(field.ty),
229
230 Node::Expr(&Expr { kind: ExprKind::Closure { .. }, .. }) => {
231 tcx.typeck(def_id).node_type(hir_id)
232 }
233
234 Node::AnonConst(_) => anon_const_type_of(&icx, def_id),
235
236 Node::ConstBlock(_) => {
237 let args = ty::GenericArgs::identity_for_item(tcx, def_id.to_def_id());
238 args.as_inline_const().ty()
239 }
240
241 Node::GenericParam(param) => match ¶m.kind {
242 GenericParamKind::Type { default: Some(ty), .. }
243 | GenericParamKind::Const { ty, .. } => icx.lower_ty(ty),
244 x => bug!("unexpected non-type Node::GenericParam: {:?}", x),
245 },
246
247 x => {
248 bug!("unexpected sort of node in type_of(): {:?}", x);
249 }
250 };
251 if let Err(e) = icx.check_tainted_by_errors()
252 && !output.references_error()
253 {
254 ty::EarlyBinder::bind(tcx, Ty::new_error(tcx, e))
255 } else {
256 ty::EarlyBinder::bind(tcx, output)
257 }
258}
259
260pub(super) fn type_of_opaque(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<'_, Ty<'_>> {
261 if let Some(def_id) = def_id.as_local() {
262 match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
263 hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
264 opaque::find_opaque_ty_constraints_for_tait(
265 tcx,
266 def_id,
267 DefiningScopeKind::MirBorrowck,
268 )
269 }
270 hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => {
271 opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
272 tcx,
273 def_id,
274 DefiningScopeKind::MirBorrowck,
275 )
276 }
277 hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
279 | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl } => {
280 if in_trait_or_impl == Some(hir::RpitContext::Trait)
281 && !tcx.defaultness(owner).has_value()
282 {
283 ::rustc_middle::util::bug::span_bug_fmt(tcx.def_span(def_id),
format_args!("tried to get type of this RPITIT with no definition"));span_bug!(
284 tcx.def_span(def_id),
285 "tried to get type of this RPITIT with no definition"
286 );
287 }
288 opaque::find_opaque_ty_constraints_for_rpit(
289 tcx,
290 def_id,
291 owner,
292 DefiningScopeKind::MirBorrowck,
293 )
294 }
295 }
296 } else {
297 tcx.type_of(def_id)
300 }
301}
302
303pub(super) fn type_of_opaque_hir_typeck(
304 tcx: TyCtxt<'_>,
305 def_id: LocalDefId,
306) -> ty::EarlyBinder<'_, Ty<'_>> {
307 match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
308 hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
309 opaque::find_opaque_ty_constraints_for_tait(tcx, def_id, DefiningScopeKind::HirTypeck)
310 }
311 hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => {
312 opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
313 tcx,
314 def_id,
315 DefiningScopeKind::HirTypeck,
316 )
317 }
318 hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
320 | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl } => {
321 if in_trait_or_impl == Some(hir::RpitContext::Trait)
322 && !tcx.defaultness(owner).has_value()
323 {
324 ::rustc_middle::util::bug::span_bug_fmt(tcx.def_span(def_id),
format_args!("tried to get type of this RPITIT with no definition"));span_bug!(
325 tcx.def_span(def_id),
326 "tried to get type of this RPITIT with no definition"
327 );
328 }
329 opaque::find_opaque_ty_constraints_for_rpit(
330 tcx,
331 def_id,
332 owner,
333 DefiningScopeKind::HirTypeck,
334 )
335 }
336 }
337}
338
339fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
340 use hir::*;
341 use rustc_middle::ty::Ty;
342 let tcx = icx.tcx;
343 let hir_id = tcx.local_def_id_to_hir_id(def_id);
344
345 let node = tcx.hir_node(hir_id);
346 let Node::AnonConst(&AnonConst { span, .. }) = node else {
347 ::rustc_middle::util::bug::span_bug_fmt(tcx.def_span(def_id),
format_args!("expected anon const in `anon_const_type_of`, got {0:?}",
node));span_bug!(
348 tcx.def_span(def_id),
349 "expected anon const in `anon_const_type_of`, got {node:?}"
350 );
351 };
352
353 let parent_node_id = tcx.parent_hir_id(hir_id);
354 let parent_node = tcx.hir_node(parent_node_id);
355
356 match parent_node {
357 Node::ConstArg(&ConstArg {
359 hir_id: arg_hir_id,
360 kind: ConstArgKind::Anon(&AnonConst { hir_id: anon_hir_id, .. }),
361 ..
362 }) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span),
363
364 Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
365 tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
366 }
367
368 Node::Field(&hir::FieldDef { default: Some(c), def_id: field_def_id, .. })
369 if c.hir_id == hir_id =>
370 {
371 tcx.type_of(field_def_id).instantiate_identity().skip_norm_wip()
372 }
373
374 _ => Ty::new_error_with_message(
375 tcx,
376 span,
377 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("unexpected anon const parent in type_of(): {0:?}",
parent_node))
})format!("unexpected anon const parent in type_of(): {parent_node:?}"),
378 ),
379 }
380}
381
382fn const_arg_anon_type_of<'tcx>(icx: &ItemCtxt<'tcx>, arg_hir_id: HirId, span: Span) -> Ty<'tcx> {
383 use hir::*;
384 use rustc_middle::ty::Ty;
385
386 let tcx = icx.tcx;
387
388 match tcx.parent_hir_node(arg_hir_id) {
389 Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. })
392 | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
393 if constant.hir_id == arg_hir_id =>
394 {
395 tcx.types.usize
396 }
397
398 Node::TyPat(pat) => {
399 let node = match tcx.parent_hir_node(pat.hir_id) {
400 Node::TyPat(p) => tcx.parent_hir_node(p.hir_id),
402 other => other,
403 };
404 let hir::TyKind::Pat(ty, _) = node.expect_ty().kind else { ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!() };
405 icx.lower_ty(ty)
406 }
407
408 _ => Ty::new_error_with_message(
411 tcx,
412 span,
413 "`type_of` called on const argument's anon const before the const argument was lowered",
414 ),
415 }
416}
417
418fn infer_placeholder_type<'tcx>(
419 cx: &dyn HirTyLowerer<'tcx>,
420 def_id: LocalDefId,
421 hir_id: HirId,
422 ty_span: Span,
423 body_span: Span,
424 item_ident: Ident,
425 kind: &'static str,
426) -> Ty<'tcx> {
427 let tcx = cx.tcx();
428 let ty = if tcx.is_type_const(def_id.to_def_id()) {
432 if let Some(trait_item_def_id) = tcx.trait_item_of(def_id.to_def_id()) {
433 tcx.type_of(trait_item_def_id).instantiate_identity().skip_norm_wip()
434 } else {
435 Ty::new_error_with_message(
436 tcx,
437 ty_span,
438 "constant with `type const` requires an explicit type",
439 )
440 }
441 } else {
442 tcx.typeck(def_id).node_type(hir_id)
443 };
444
445 let guar = cx
450 .dcx()
451 .try_steal_modify_and_emit_err(ty_span, StashKey::ItemNoType, |err| {
452 if ty_span.from_expansion() {
459 return;
460 }
461 if !ty.references_error() {
462 let colon = if ty_span == item_ident.span.shrink_to_hi() { ":" } else { "" };
464
465 if let Suggestions::Enabled(suggestions) = &mut err.suggestions {
468 suggestions.clear();
469 }
470
471 if let Some(ty) = ty.make_suggestable(tcx, false, None) {
472 err.span_suggestion(
473 ty_span,
474 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("provide a type for the {0}", kind))
})format!("provide a type for the {kind}"),
475 {
let _guard =
::rustc_middle::ty::print::pretty::RtnModeHelper::with(RtnMode::ForSuggestion);
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} {1}", colon, ty))
})
}with_types_for_suggestion!(format!("{colon} {ty}")),
476 Applicability::MachineApplicable,
477 );
478 } else {
479 {
let _guard = ForceTrimmedGuard::new();
err.span_note(body_span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("however, the inferred type `{0}` cannot be named",
ty))
}))
};with_forced_trimmed_paths!(err.span_note(
480 body_span,
481 format!("however, the inferred type `{ty}` cannot be named"),
482 ));
483 }
484 }
485 })
486 .unwrap_or_else(|| {
487 let mut visitor = HirPlaceholderCollector::default();
488 let node = tcx.hir_node_by_def_id(def_id);
489 if let Some(ty) = node.ty() {
490 visitor.visit_ty_unambig(ty);
491 }
492 if visitor.spans.is_empty() {
494 visitor.spans.push(ty_span);
495 }
496 let mut diag = bad_placeholder(cx, visitor.spans, kind);
497
498 if ty_span.is_empty() && ty_span.from_expansion() {
504 diag.primary_message("missing type for item");
506 } else if !ty.references_error() {
507 if let Some(ty) = ty.make_suggestable(tcx, false, None) {
508 diag.span_suggestion_verbose(
509 ty_span,
510 "replace this with a fully-specified type",
511 ty,
512 Applicability::MachineApplicable,
513 );
514 } else {
515 {
let _guard = ForceTrimmedGuard::new();
diag.span_note(body_span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("however, the inferred type `{0}` cannot be named",
ty))
}))
};with_forced_trimmed_paths!(diag.span_note(
516 body_span,
517 format!("however, the inferred type `{ty}` cannot be named"),
518 ));
519 }
520 }
521
522 diag.emit()
523 });
524 Ty::new_error(tcx, guar)
525}
526
527fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) {
528 if !tcx.features().inherent_associated_types() {
529 use rustc_session::diagnostics::feature_err;
530 use rustc_span::sym;
531 feature_err(
532 &tcx.sess,
533 sym::inherent_associated_types,
534 span,
535 "inherent associated types are unstable",
536 )
537 .emit();
538 }
539}
540
541pub(crate) fn type_alias_is_checked<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
542 use hir::intravisit::Visitor;
543 if tcx.features().checked_type_aliases() {
544 return true;
545 }
546 struct HasTait;
547 impl<'tcx> Visitor<'tcx> for HasTait {
548 type Result = ControlFlow<()>;
549 fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
550 if let hir::TyKind::OpaqueDef(..) = t.kind {
551 ControlFlow::Break(())
552 } else {
553 hir::intravisit::walk_ty(self, t)
554 }
555 }
556 }
557 HasTait.visit_ty_unambig(tcx.hir_expect_item(def_id).expect_ty_alias().2).is_break()
558}