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