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